1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2001
21 * the Initial Developer. All Rights Reserved.
24 * Stuart Parmenter <stuart@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "imgRequestProxy.h"
42 #include "nsIInputStream.h"
43 #include "nsIComponentManager.h"
44 #include "nsIServiceManager.h"
45 #include "nsIMultiPartChannel.h"
48 #include "nsXPIDLString.h"
49 #include "nsReadableUtils.h"
52 #include "ImageErrors.h"
53 #include "ImageLogging.h"
58 NS_IMPL_ISUPPORTS3(imgRequestProxy
, imgIRequest
, nsIRequest
,
61 imgRequestProxy::imgRequestProxy() :
64 mLoadFlags(nsIRequest::LOAD_NORMAL
),
66 mIsInLoadGroup(PR_FALSE
),
67 mListenerIsStrongRef(PR_FALSE
)
69 /* member initializers and constructor code */
73 imgRequestProxy::~imgRequestProxy()
76 NS_PRECONDITION(!mListener
, "Someone forgot to properly cancel this request!");
77 // Explicitly set mListener to null to ensure that the RemoveProxy
78 // call below can't send |this| to an arbitrary listener while |this|
79 // is being destroyed. This is all belt-and-suspenders in view of the
87 /* Call RemoveProxy with a successful status. This will keep the
88 channel, if still downloading data, from being canceled if 'this' is
89 the last observer. This allows the image to continue to download and
90 be cached even if no one is using it currently.
92 Passing false to aNotify means that we will still get
93 OnStopRequest, if needed.
95 mOwner
->RemoveProxy(this, NS_OK
, PR_FALSE
);
102 nsresult
imgRequestProxy::Init(imgRequest
*request
, nsILoadGroup
*aLoadGroup
, imgIDecoderObserver
*aObserver
)
104 NS_PRECONDITION(!mOwner
&& !mListener
, "imgRequestProxy is already initialized");
105 NS_PRECONDITION(request
, "no request");
107 return NS_ERROR_NULL_POINTER
;
109 LOG_SCOPE_WITH_PARAM(gImgLog
, "imgRequestProxy::Init", "request", request
);
112 mListener
= aObserver
;
113 // Make sure to addref mListener before the AddProxy call below, since
114 // that call might well want to release it if the imgRequest has
115 // already seen OnStopRequest.
117 mListenerIsStrongRef
= PR_TRUE
;
118 NS_ADDREF(mListener
);
120 mLoadGroup
= aLoadGroup
;
122 // Note: AddProxy won't send all the On* notifications immediatly
123 request
->AddProxy(this);
128 nsresult
imgRequestProxy::ChangeOwner(imgRequest
*aNewOwner
)
133 // Passing false to aNotify means that mListener will still get
134 // OnStopRequest, if needed.
135 mOwner
->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER
, PR_FALSE
);
139 mOwner
->AddProxy(this);
144 void imgRequestProxy::AddToLoadGroup()
146 NS_ASSERTION(!mIsInLoadGroup
, "Whaa, we're already in the loadgroup!");
148 if (!mIsInLoadGroup
&& mLoadGroup
) {
149 mLoadGroup
->AddRequest(this, nsnull
);
150 mIsInLoadGroup
= PR_TRUE
;
154 void imgRequestProxy::RemoveFromLoadGroup(PRBool releaseLoadGroup
)
159 /* calling RemoveFromLoadGroup may cause the document to finish
160 loading, which could result in our death. We need to make sure
161 that we stay alive long enough to fight another battle... at
162 least until we exit this function.
164 nsCOMPtr
<imgIRequest
> kungFuDeathGrip(this);
166 mLoadGroup
->RemoveRequest(this, NS_OK
, nsnull
);
167 mIsInLoadGroup
= PR_FALSE
;
169 if (releaseLoadGroup
) {
170 // We're done with the loadgroup, release it.
176 /** nsIRequest / imgIRequest methods **/
178 /* readonly attribute wstring name; */
179 NS_IMETHODIMP
imgRequestProxy::GetName(nsACString
&aName
)
183 nsCOMPtr
<nsIURI
> uri
;
184 mOwner
->GetURI(getter_AddRefs(uri
));
191 /* boolean isPending (); */
192 NS_IMETHODIMP
imgRequestProxy::IsPending(PRBool
*_retval
)
194 return NS_ERROR_NOT_IMPLEMENTED
;
197 /* readonly attribute nsresult status; */
198 NS_IMETHODIMP
imgRequestProxy::GetStatus(nsresult
*aStatus
)
201 return NS_ERROR_FAILURE
;
203 *aStatus
= mOwner
->GetNetworkStatus();
208 /* void cancel (in nsresult status); */
209 NS_IMETHODIMP
imgRequestProxy::Cancel(nsresult status
)
211 if (mCanceled
|| !mOwner
)
212 return NS_ERROR_FAILURE
;
214 LOG_SCOPE(gImgLog
, "imgRequestProxy::Cancel");
218 // Passing false to aNotify means that mListener will still get
219 // OnStopRequest, if needed.
220 mOwner
->RemoveProxy(this, status
, PR_FALSE
);
227 /* void suspend (); */
228 NS_IMETHODIMP
imgRequestProxy::Suspend()
230 return NS_ERROR_NOT_IMPLEMENTED
;
233 /* void resume (); */
234 NS_IMETHODIMP
imgRequestProxy::Resume()
236 return NS_ERROR_NOT_IMPLEMENTED
;
239 /* attribute nsILoadGroup loadGroup */
240 NS_IMETHODIMP
imgRequestProxy::GetLoadGroup(nsILoadGroup
**loadGroup
)
242 NS_IF_ADDREF(*loadGroup
= mLoadGroup
.get());
245 NS_IMETHODIMP
imgRequestProxy::SetLoadGroup(nsILoadGroup
*loadGroup
)
247 mLoadGroup
= loadGroup
;
251 /* attribute nsLoadFlags loadFlags */
252 NS_IMETHODIMP
imgRequestProxy::GetLoadFlags(nsLoadFlags
*flags
)
257 NS_IMETHODIMP
imgRequestProxy::SetLoadFlags(nsLoadFlags flags
)
263 /** imgIRequest methods **/
265 /* attribute imgIContainer image; */
266 NS_IMETHODIMP
imgRequestProxy::GetImage(imgIContainer
* *aImage
)
269 return NS_ERROR_FAILURE
;
271 mOwner
->GetImage(aImage
);
275 /* readonly attribute unsigned long imageStatus; */
276 NS_IMETHODIMP
imgRequestProxy::GetImageStatus(PRUint32
*aStatus
)
279 *aStatus
= imgIRequest::STATUS_ERROR
;
280 return NS_ERROR_FAILURE
;
283 *aStatus
= mOwner
->GetImageStatus();
287 /* readonly attribute nsIURI URI; */
288 NS_IMETHODIMP
imgRequestProxy::GetURI(nsIURI
**aURI
)
291 return NS_ERROR_FAILURE
;
293 return mOwner
->GetURI(aURI
);
296 /* readonly attribute imgIDecoderObserver decoderObserver; */
297 NS_IMETHODIMP
imgRequestProxy::GetDecoderObserver(imgIDecoderObserver
**aDecoderObserver
)
299 *aDecoderObserver
= mListener
;
300 NS_IF_ADDREF(*aDecoderObserver
);
304 /* readonly attribute string mimeType; */
305 NS_IMETHODIMP
imgRequestProxy::GetMimeType(char **aMimeType
)
308 return NS_ERROR_FAILURE
;
310 const char *type
= mOwner
->GetMimeType();
312 return NS_ERROR_FAILURE
;
314 *aMimeType
= nsCRT::strdup(type
);
319 NS_IMETHODIMP
imgRequestProxy::Clone(imgIDecoderObserver
* aObserver
,
320 imgIRequest
** aClone
)
322 NS_PRECONDITION(aClone
, "Null out param");
324 imgRequestProxy
* clone
= new imgRequestProxy();
326 return NS_ERROR_OUT_OF_MEMORY
;
330 // It is important to call |SetLoadFlags()| before calling |Init()| because
331 // |Init()| adds the request to the loadgroup.
332 // When a request is added to a loadgroup, its load flags are merged
333 // with the load flags of the loadgroup.
334 // XXXldb That's not true anymore. Stuff from imgLoader adds the
335 // request to the loadgroup.
336 clone
->SetLoadFlags(mLoadFlags
);
337 nsresult rv
= clone
->Init(mOwner
, mLoadGroup
, aObserver
);
343 // Assign to *aClone before calling NotifyProxyListener so that if
344 // the caller expects to only be notified for requests it's already
345 // holding pointers to it won't be surprised.
348 // Send the notifications to the clone's observer
349 mOwner
->NotifyProxyListener(clone
);
354 /* readonly attribute nsIPrincipal imagePrincipal; */
355 NS_IMETHODIMP
imgRequestProxy::GetImagePrincipal(nsIPrincipal
**aPrincipal
)
358 return NS_ERROR_FAILURE
;
360 return mOwner
->GetPrincipal(aPrincipal
);
363 /** nsISupportsPriority methods **/
365 NS_IMETHODIMP
imgRequestProxy::GetPriority(PRInt32
*priority
)
367 NS_ENSURE_STATE(mOwner
);
368 *priority
= mOwner
->Priority();
372 NS_IMETHODIMP
imgRequestProxy::SetPriority(PRInt32 priority
)
374 NS_ENSURE_STATE(mOwner
&& !mCanceled
);
375 mOwner
->AdjustPriority(this, priority
- mOwner
->Priority());
379 NS_IMETHODIMP
imgRequestProxy::AdjustPriority(PRInt32 priority
)
381 NS_ENSURE_STATE(mOwner
&& !mCanceled
);
382 mOwner
->AdjustPriority(this, priority
);
386 /** imgIContainerObserver methods **/
388 void imgRequestProxy::FrameChanged(imgIContainer
*container
, gfxIImageFrame
*newframe
, nsIntRect
* dirtyRect
)
390 LOG_FUNC(gImgLog
, "imgRequestProxy::FrameChanged");
393 // Hold a ref to the listener while we call it, just in case.
394 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
395 mListener
->FrameChanged(container
, newframe
, dirtyRect
);
399 /** imgIDecoderObserver methods **/
401 void imgRequestProxy::OnStartDecode()
403 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStartDecode");
406 // Hold a ref to the listener while we call it, just in case.
407 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
408 mListener
->OnStartDecode(this);
412 void imgRequestProxy::OnStartContainer(imgIContainer
*image
)
414 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStartContainer");
417 // Hold a ref to the listener while we call it, just in case.
418 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
419 mListener
->OnStartContainer(this, image
);
423 void imgRequestProxy::OnStartFrame(gfxIImageFrame
*frame
)
425 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStartFrame");
428 // Hold a ref to the listener while we call it, just in case.
429 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
430 mListener
->OnStartFrame(this, frame
);
434 void imgRequestProxy::OnDataAvailable(gfxIImageFrame
*frame
, const nsIntRect
* rect
)
436 LOG_FUNC(gImgLog
, "imgRequestProxy::OnDataAvailable");
439 // Hold a ref to the listener while we call it, just in case.
440 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
441 mListener
->OnDataAvailable(this, frame
, rect
);
445 void imgRequestProxy::OnStopFrame(gfxIImageFrame
*frame
)
447 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStopFrame");
450 // Hold a ref to the listener while we call it, just in case.
451 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
452 mListener
->OnStopFrame(this, frame
);
456 void imgRequestProxy::OnStopContainer(imgIContainer
*image
)
458 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStopContainer");
461 // Hold a ref to the listener while we call it, just in case.
462 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
463 mListener
->OnStopContainer(this, image
);
467 void imgRequestProxy::OnStopDecode(nsresult status
, const PRUnichar
*statusArg
)
469 LOG_FUNC(gImgLog
, "imgRequestProxy::OnStopDecode");
472 // Hold a ref to the listener while we call it, just in case.
473 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
474 mListener
->OnStopDecode(this, status
, statusArg
);
480 void imgRequestProxy::OnStartRequest(nsIRequest
*request
, nsISupports
*ctxt
)
485 LOG_FUNC_WITH_PARAM(gImgLog
, "imgRequestProxy::OnStartRequest", "name", name
.get());
489 // Hold a ref to the listener while we call it, just in case.
490 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
491 mListener
->OnStartRequest(this);
495 void imgRequestProxy::OnStopRequest(nsIRequest
*request
, nsISupports
*ctxt
,
496 nsresult statusCode
, PRBool lastPart
)
501 LOG_FUNC_WITH_PARAM(gImgLog
, "imgRequestProxy::OnStopRequest", "name", name
.get());
503 // There's all sorts of stuff here that could kill us (the OnStopRequest call
504 // on the listener, the removal from the loadgroup, the release of the
505 // listener, etc). Don't let them do it.
506 nsCOMPtr
<imgIRequest
> kungFuDeathGrip(this);
509 // Hold a ref to the listener while we call it, just in case.
510 nsCOMPtr
<imgIDecoderObserver
> kungFuDeathGrip(mListener
);
511 mListener
->OnStopRequest(this, lastPart
);
514 // If we're expecting more data from a multipart channel, re-add ourself
515 // to the loadgroup so that the document doesn't lose track of the load.
516 // If the request is already a background request and there's more data
517 // coming, we can just leave the request in the loadgroup as-is.
518 if (lastPart
|| (mLoadFlags
& nsIRequest::LOAD_BACKGROUND
) == 0) {
519 RemoveFromLoadGroup(lastPart
);
520 // More data is coming, so change the request to be a background request
521 // and put it back in the loadgroup.
523 mLoadFlags
|= nsIRequest::LOAD_BACKGROUND
;
528 if (mListenerIsStrongRef
) {
529 NS_PRECONDITION(mListener
, "How did that happen?");
530 // Drop our strong ref to the listener now that we're done with
531 // everything. Note that this can cancel us and other fun things
532 // like that. Don't add anything in this method after this point.
533 imgIDecoderObserver
* obs
= mListener
;
534 mListenerIsStrongRef
= PR_FALSE
;
539 void imgRequestProxy::NullOutListener()
541 if (mListenerIsStrongRef
) {
542 // Releasing could do weird reentery stuff, so just play it super-safe
543 nsCOMPtr
<imgIDecoderObserver
> obs
;
545 mListenerIsStrongRef
= PR_FALSE
;