Back to index

lightning-sunbird  0.9+nobinonly
imgRequest.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "imgRequest.h"
00041 
00042 #include "imgLoader.h"
00043 #include "imgCache.h"
00044 #include "imgRequestProxy.h"
00045 
00046 #include "imgILoader.h"
00047 #include "ImageErrors.h"
00048 #include "ImageLogging.h"
00049 
00050 #include "gfxIImageFrame.h"
00051 
00052 #include "netCore.h"
00053 
00054 #include "nsIChannel.h"
00055 #include "nsICachingChannel.h"
00056 #include "nsILoadGroup.h"
00057 #include "nsIInputStream.h"
00058 #include "nsIMultiPartChannel.h"
00059 #include "nsIHttpChannel.h"
00060 
00061 #include "nsIComponentManager.h"
00062 #include "nsIProxyObjectManager.h"
00063 #include "nsIServiceManager.h"
00064 #include "nsISupportsPrimitives.h"
00065 
00066 #include "nsAutoLock.h"
00067 #include "nsString.h"
00068 #include "nsXPIDLString.h"
00069 #include "plstr.h" // PL_strcasestr(...)
00070 
00071 #if defined(PR_LOGGING)
00072 PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
00073 #endif
00074 
00075 NS_IMPL_THREADSAFE_ISUPPORTS6(imgRequest, imgILoad,
00076                               imgIDecoderObserver, imgIContainerObserver,
00077                               nsIStreamListener, nsIRequestObserver,
00078                               nsISupportsWeakReference)
00079 
00080 imgRequest::imgRequest() : 
00081   mObservers(0),
00082   mLoading(PR_FALSE), mProcessing(PR_FALSE), mHadLastPart(PR_FALSE),
00083   mImageStatus(imgIRequest::STATUS_NONE), mState(0),
00084   mCacheId(0), mValidator(nsnull), mIsMultiPartChannel(PR_FALSE)
00085 {
00086   /* member initializers and constructor code */
00087 }
00088 
00089 imgRequest::~imgRequest()
00090 {
00091   /* destructor code */
00092 }
00093 
00094 nsresult imgRequest::Init(nsIChannel *aChannel,
00095                           nsICacheEntryDescriptor *aCacheEntry,
00096                           void *aCacheId,
00097                           void *aLoadId)
00098 {
00099   LOG_FUNC(gImgLog, "imgRequest::Init");
00100 
00101   NS_ASSERTION(!mImage, "imgRequest::Init -- Multiple calls to init");
00102   NS_ASSERTION(aChannel, "imgRequest::Init -- No channel");
00103 
00104   mProperties = do_CreateInstance("@mozilla.org/properties;1");
00105   if (!mProperties)
00106     return NS_ERROR_OUT_OF_MEMORY;
00107 
00108   mChannel = aChannel;
00109 
00110   /* set our loading flag to true here.
00111      Setting it here lets checks to see if the load is in progress
00112      before OnStartRequest gets called, letting 'this' properly get removed
00113      from the cache in certain cases.
00114   */
00115   mLoading = PR_TRUE;
00116 
00117   mCacheEntry = aCacheEntry;
00118 
00119   mCacheId = aCacheId;
00120 
00121   SetLoadId(aLoadId);
00122 
00123   return NS_OK;
00124 }
00125 
00126 nsresult imgRequest::AddProxy(imgRequestProxy *proxy, PRBool aNotify)
00127 {
00128   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
00129 
00130   mObservers.AppendElement(NS_STATIC_CAST(void*, proxy));
00131 
00132   if (aNotify)
00133     NotifyProxyListener(proxy);
00134 
00135   return NS_OK;
00136 }
00137 
00138 nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify)
00139 {
00140   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
00141 
00142   mObservers.RemoveElement(NS_STATIC_CAST(void*, proxy));
00143 
00144   /* Check mState below before we potentially call Cancel() below. Since
00145      Cancel() may result in OnStopRequest being called back before Cancel()
00146      returns, leaving mState in a different state then the one it was in at
00147      this point.
00148    */
00149 
00150   if (aNotify) {
00151     // make sure that observer gets an OnStopDecode message sent to it
00152     if (!(mState & onStopDecode)) {
00153       proxy->OnStopDecode(aStatus, nsnull);
00154     }
00155 
00156   }
00157 
00158   // make sure that observer gets an OnStopRequest message sent to it
00159   if (!(mState & onStopRequest)) {
00160     proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED, PR_TRUE);
00161   }
00162 
00163   if (mImage && !HaveProxyWithObserver(nsnull)) {
00164     LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation");
00165 
00166     mImage->StopAnimation();
00167   }
00168 
00169   if (mObservers.Count() == 0) {
00170     /* If |aStatus| is a failure code, then cancel the load if it is still in progress.
00171        Otherwise, let the load continue, keeping 'this' in the cache with no observers.
00172        This way, if a proxy is destroyed without calling cancel on it, it won't leak
00173        and won't leave a bad pointer in mObservers.
00174      */
00175     if (mChannel && mLoading && NS_FAILED(aStatus)) {
00176       LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress.  canceling");
00177 
00178       mImageStatus |= imgIRequest::STATUS_LOAD_PARTIAL;
00179 
00180       this->Cancel(NS_BINDING_ABORTED);
00181     }
00182 
00183     /* break the cycle from the cache entry. */
00184     mCacheEntry = nsnull;
00185   }
00186 
00187   // If a proxy is removed for a reason other than its owner being
00188   // changed, remove the proxy from the loadgroup.
00189   if (aStatus != NS_IMAGELIB_CHANGING_OWNER)
00190     proxy->RemoveFromLoadGroup(PR_TRUE);
00191 
00192   return NS_OK;
00193 }
00194 
00195 nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
00196 {
00197   nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
00198 
00199   // OnStartRequest
00200   if (mState & onStartRequest)
00201     proxy->OnStartRequest(nsnull, nsnull);
00202 
00203   // OnStartDecode
00204   if (mState & onStartDecode)
00205     proxy->OnStartDecode();
00206 
00207   // OnStartContainer
00208   if (mState & onStartContainer)
00209     proxy->OnStartContainer(mImage);
00210 
00211   // Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
00212   PRUint32 nframes = 0;
00213   if (mImage)
00214     mImage->GetNumFrames(&nframes);
00215 
00216   if (nframes > 0) {
00217     nsCOMPtr<gfxIImageFrame> frame;
00218 
00219     // get the current frame or only frame
00220     mImage->GetCurrentFrame(getter_AddRefs(frame));
00221     NS_ASSERTION(frame, "GetCurrentFrame gave back a null frame!");
00222 
00223     // OnStartFrame
00224     proxy->OnStartFrame(frame);
00225 
00226     if (!(mState & onStopContainer)) {
00227       // OnDataAvailable
00228       nsIntRect r;
00229       frame->GetRect(r);  // XXX we should only send the currently decoded rectangle here.
00230       proxy->OnDataAvailable(frame, &r);
00231     } else {
00232       // OnDataAvailable
00233       nsIntRect r;
00234       frame->GetRect(r);  // We're done loading this image, send the the whole rect
00235       proxy->OnDataAvailable(frame, &r);
00236 
00237       // OnStopFrame
00238       proxy->OnStopFrame(frame);
00239     }
00240   }
00241 
00242   // OnStopContainer
00243   if (mState & onStopContainer)
00244     proxy->OnStopContainer(mImage);
00245 
00246   // OnStopDecode
00247   if (mState & onStopDecode)
00248     proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
00249 
00250   if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
00251     LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation");
00252 
00253     mImage->ResetAnimation();
00254   }
00255 
00256   if (mState & onStopRequest) {
00257     proxy->OnStopRequest(nsnull, nsnull,
00258                          GetResultFromImageStatus(mImageStatus),
00259                          mHadLastPart);
00260   }
00261 
00262   return NS_OK;
00263 }
00264 
00265 nsresult imgRequest::GetResultFromImageStatus(PRUint32 aStatus) const
00266 {
00267   nsresult rv = NS_OK;
00268 
00269   if (aStatus & imgIRequest::STATUS_ERROR)
00270     rv = NS_IMAGELIB_ERROR_FAILURE;
00271   else if (aStatus & imgIRequest::STATUS_LOAD_COMPLETE)
00272     rv = NS_IMAGELIB_SUCCESS_LOAD_FINISHED;
00273 
00274   return rv;
00275 }
00276 
00277 void imgRequest::Cancel(nsresult aStatus)
00278 {
00279   /* The Cancel() method here should only be called by this class. */
00280 
00281   LOG_SCOPE(gImgLog, "imgRequest::Cancel");
00282 
00283   if (mImage) {
00284     LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
00285 
00286     mImage->StopAnimation();
00287   }
00288 
00289   if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
00290     mImageStatus |= imgIRequest::STATUS_ERROR;
00291 
00292   RemoveFromCache();
00293 
00294   if (mChannel && mLoading)
00295     mChannel->Cancel(aStatus);
00296 }
00297 
00298 nsresult imgRequest::GetURI(nsIURI **aURI)
00299 {
00300   LOG_FUNC(gImgLog, "imgRequest::GetURI");
00301 
00302   if (mChannel)
00303     return mChannel->GetOriginalURI(aURI);
00304 
00305   if (mURI) {
00306     *aURI = mURI;
00307     NS_ADDREF(*aURI);
00308     return NS_OK;
00309   }
00310 
00311   return NS_ERROR_FAILURE;
00312 }
00313 
00314 void imgRequest::RemoveFromCache()
00315 {
00316   LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
00317 
00318   if (mCacheEntry) {
00319     mCacheEntry->Doom();
00320     mCacheEntry = nsnull;
00321   }
00322 }
00323 
00324 PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
00325 {
00326   for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
00327     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00328     if (proxy == aProxyToIgnore) {
00329       continue;
00330     }
00331     
00332     if (proxy->HasObserver()) {
00333       return PR_TRUE;
00334     }
00335   }
00336   
00337   return PR_FALSE;
00338 }
00339 
00340 PRInt32 imgRequest::Priority() const
00341 {
00342   PRInt32 priority = nsISupportsPriority::PRIORITY_NORMAL;
00343   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
00344   if (p)
00345     p->GetPriority(&priority);
00346   return priority;
00347 }
00348 
00349 void imgRequest::AdjustPriority(imgRequestProxy *proxy, PRInt32 delta)
00350 {
00351   // only the first proxy is allowed to modify the priority of this image load.
00352   //
00353   // XXX(darin): this is probably not the most optimal algorithm as we may want
00354   // to increase the priority of requests that have a lot of proxies.  the key
00355   // concern though is that image loads remain lower priority than other pieces
00356   // of content such as link clicks, CSS, and JS.
00357   //
00358   if (mObservers[0] != proxy)
00359     return;
00360 
00361   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
00362   if (p)
00363     p->AdjustPriority(delta);
00364 }
00365 
00368 NS_IMETHODIMP imgRequest::SetImage(imgIContainer *aImage)
00369 {
00370   LOG_FUNC(gImgLog, "imgRequest::SetImage");
00371 
00372   mImage = aImage;
00373 
00374   return NS_OK;
00375 }
00376 
00377 NS_IMETHODIMP imgRequest::GetImage(imgIContainer **aImage)
00378 {
00379   LOG_FUNC(gImgLog, "imgRequest::GetImage");
00380 
00381   *aImage = mImage;
00382   NS_IF_ADDREF(*aImage);
00383   return NS_OK;
00384 }
00385 
00386 NS_IMETHODIMP imgRequest::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
00387 {
00388   LOG_FUNC(gImgLog, "imgRequest::GetIsMultiPartChannel");
00389 
00390   *aIsMultiPartChannel = mIsMultiPartChannel;
00391 
00392   return NS_OK;
00393 }
00394 
00397 /* [noscript] void frameChanged (in imgIContainer container, in gfxIImageFrame newframe, in nsIntRect dirtyRect); */
00398 NS_IMETHODIMP imgRequest::FrameChanged(imgIContainer *container,
00399                                        gfxIImageFrame *newframe,
00400                                        nsIntRect * dirtyRect)
00401 {
00402   LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
00403 
00404   PRInt32 count = mObservers.Count();
00405   for (PRInt32 i = 0; i < count; i++) {
00406     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00407     if (proxy) proxy->FrameChanged(container, newframe, dirtyRect);
00408 
00409     // If this assertion fires, it means that imgRequest notifications could
00410     // be dropped!
00411     NS_ASSERTION(count == mObservers.Count(),
00412                  "The observer list changed while being iterated over!");
00413   }
00414 
00415   return NS_OK;
00416 }
00417 
00420 /* void onStartDecode (in imgIRequest request); */
00421 NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
00422 {
00423   LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
00424 
00425   mState |= onStartDecode;
00426 
00427   PRInt32 count = mObservers.Count();
00428   for (PRInt32 i = 0; i < count; i++) {
00429     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00430     if (proxy) proxy->OnStartDecode();
00431 
00432     // If this assertion fires, it means that imgRequest notifications could
00433     // be dropped!
00434     NS_ASSERTION(count == mObservers.Count(), 
00435                  "The observer list changed while being iterated over!");
00436   }
00437 
00438   /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
00439      indicates the beginning of a new decode.
00440      The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
00441      the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
00442    */
00443   if (mCacheEntry)
00444     mCacheEntry->SetDataSize(0);
00445 
00446   return NS_OK;
00447 }
00448 
00449 /* void onStartContainer (in imgIRequest request, in imgIContainer image); */
00450 NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
00451 {
00452   LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer");
00453 
00454   NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
00455   if (!image) return NS_ERROR_UNEXPECTED;
00456 
00457   mState |= onStartContainer;
00458 
00459   mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
00460 
00461   PRInt32 count = mObservers.Count();
00462   for (PRInt32 i = 0; i < count; i++) {
00463     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00464     if (proxy) proxy->OnStartContainer(image);
00465 
00466     // If this assertion fires, it means that imgRequest notifications could
00467     // be dropped!
00468     NS_ASSERTION(count == mObservers.Count(), 
00469                  "The observer list changed while being iterated over!");
00470 
00471   }
00472 
00473   return NS_OK;
00474 }
00475 
00476 /* void onStartFrame (in imgIRequest request, in gfxIImageFrame frame); */
00477 NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
00478                                        gfxIImageFrame *frame)
00479 {
00480   LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
00481 
00482   PRInt32 count = mObservers.Count();
00483   for (PRInt32 i = 0; i < count; i++) {
00484     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00485     if (proxy) proxy->OnStartFrame(frame);
00486 
00487     // If this assertion fires, it means that imgRequest notifications could
00488     // be dropped!
00489     NS_ASSERTION(count == mObservers.Count(), 
00490                  "The observer list changed while being iterated over!");
00491   }
00492 
00493   return NS_OK;
00494 }
00495 
00496 /* [noscript] void onDataAvailable (in imgIRequest request, in gfxIImageFrame frame, [const] in nsIntRect rect); */
00497 NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
00498                                           gfxIImageFrame *frame,
00499                                           const nsIntRect * rect)
00500 {
00501   LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
00502 
00503   PRInt32 count = mObservers.Count();
00504   for (PRInt32 i = 0; i < count; i++) {
00505     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00506     if (proxy) proxy->OnDataAvailable(frame, rect);
00507 
00508     // If this assertion fires, it means that imgRequest notifications could
00509     // be dropped!
00510     NS_ASSERTION(count == mObservers.Count(), 
00511                  "The observer list changed while being iterated over!");
00512   }
00513 
00514   return NS_OK;
00515 }
00516 
00517 /* void onStopFrame (in imgIRequest request, in gfxIImageFrame frame); */
00518 NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
00519                                       gfxIImageFrame *frame)
00520 {
00521   NS_ASSERTION(frame, "imgRequest::OnStopFrame called with NULL frame");
00522   if (!frame) return NS_ERROR_UNEXPECTED;
00523 
00524   LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
00525 
00526   mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
00527 
00528   if (mCacheEntry) {
00529     PRUint32 cacheSize = 0;
00530 
00531     mCacheEntry->GetDataSize(&cacheSize);
00532 
00533     PRUint32 imageSize = 0;
00534     PRUint32 alphaSize = 0;
00535 
00536     frame->GetImageDataLength(&imageSize);
00537     frame->GetAlphaDataLength(&alphaSize);
00538 
00539     mCacheEntry->SetDataSize(cacheSize + imageSize + alphaSize);
00540   }
00541 
00542   PRInt32 count = mObservers.Count();
00543   for (PRInt32 i = 0; i < count; i++) {
00544     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00545     if (proxy) proxy->OnStopFrame(frame);
00546 
00547     // If this assertion fires, it means that imgRequest notifications could
00548     // be dropped!
00549     NS_ASSERTION(count == mObservers.Count(), 
00550                  "The observer list changed while being iterated over!");
00551   }
00552 
00553   return NS_OK;
00554 }
00555 
00556 /* void onStopContainer (in imgIRequest request, in imgIContainer image); */
00557 NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
00558                                           imgIContainer *image)
00559 {
00560   LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
00561 
00562   mState |= onStopContainer;
00563 
00564   PRInt32 count = mObservers.Count();
00565   for (PRInt32 i = 0; i < count; i++) {
00566     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00567     if (proxy) proxy->OnStopContainer(image);
00568 
00569     // If this assertion fires, it means that imgRequest notifications could
00570     // be dropped!
00571     NS_ASSERTION(count == mObservers.Count(), 
00572                  "The observer list changed while being iterated over!");
00573   }
00574 
00575   return NS_OK;
00576 }
00577 
00578 /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
00579 NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
00580                                        nsresult aStatus,
00581                                        const PRUnichar *aStatusArg)
00582 {
00583   LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
00584 
00585   NS_ASSERTION(!(mState & onStopDecode), "OnStopDecode called multiple times.");
00586 
00587   mState |= onStopDecode;
00588 
00589   if (NS_FAILED(aStatus) && !(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) {
00590     mImageStatus |= imgIRequest::STATUS_ERROR;
00591   }
00592 
00593   PRInt32 count = mObservers.Count();
00594   for (PRInt32 i = 0; i < count; i++) {
00595     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00596     if (proxy) proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
00597 
00598     // If this assertion fires, it means that imgRequest notifications could
00599     // be dropped!
00600     NS_ASSERTION(count == mObservers.Count(), 
00601                  "The observer list changed while being iterated over!");
00602   }
00603 
00604   return NS_OK;
00605 }
00606 
00609 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
00610 NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
00611 {
00612   nsresult rv;
00613 
00614   LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
00615 
00616   NS_ASSERTION(!mDecoder, "imgRequest::OnStartRequest -- we already have a decoder");
00617 
00618   /* if mChannel isn't set here, use aRequest.
00619      Having mChannel set is important for Canceling the load, and since we set
00620      mChannel to null in OnStopRequest.  Since with multipart/x-mixed-replace, you
00621      can get multiple OnStartRequests, we need to reinstance mChannel so that when/if
00622      Cancel() gets called, we have a channel to cancel and we don't leave the channel
00623      open forever.
00624    */
00625   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
00626   if (!mChannel) {
00627     if (mpchan)
00628       mpchan->GetBaseChannel(getter_AddRefs(mChannel));
00629     else
00630       mChannel = do_QueryInterface(aRequest);
00631   }
00632 
00633   if (mpchan)
00634       mIsMultiPartChannel = PR_TRUE;
00635 
00636   /* set our state variables to their initial values, but advance mState
00637      to onStartRequest. */
00638   mImageStatus = imgIRequest::STATUS_NONE;
00639   mState = onStartRequest;
00640 
00641   /* set our loading flag to true */
00642   mLoading = PR_TRUE;
00643 
00644   /* notify our kids */
00645   PRInt32 count = mObservers.Count();
00646   for (PRInt32 i = 0; i < count; i++) {
00647     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00648     if (proxy) proxy->OnStartRequest(aRequest, ctxt);
00649 
00650     // If this assertion fires, it means that imgRequest notifications could
00651     // be dropped!
00652     NS_ASSERTION(count == mObservers.Count(), 
00653                  "The observer list changed while being iterated over!");
00654   }
00655 
00656   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
00657 
00658   /* get the expires info */
00659   if (mCacheEntry) {
00660     nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aRequest));
00661     if (cacheChannel) {
00662       nsCOMPtr<nsISupports> cacheToken;
00663       cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
00664       if (cacheToken) {
00665         nsCOMPtr<nsICacheEntryInfo> entryDesc(do_QueryInterface(cacheToken));
00666         if (entryDesc) {
00667           PRUint32 expiration;
00668           /* get the expiration time from the caching channel's token */
00669           entryDesc->GetExpirationTime(&expiration);
00670 
00671           /* set the expiration time on our entry */
00672           mCacheEntry->SetExpirationTime(expiration);
00673         }
00674       }
00675     }
00676     //
00677     // Determine whether the cache entry must be revalidated when it expires.
00678     // If so, then the cache entry must *not* be used during HISTORY loads if
00679     // it has expired.
00680     //
00681     // Currently, only HTTP specifies this information...
00682     //
00683     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
00684     if (httpChannel) {
00685       PRBool bMustRevalidate = PR_FALSE;
00686 
00687       rv = httpChannel->IsNoStoreResponse(&bMustRevalidate);
00688 
00689       if (!bMustRevalidate) {
00690         rv = httpChannel->IsNoCacheResponse(&bMustRevalidate);
00691       }
00692 
00693       if (!bMustRevalidate) {
00694         nsCAutoString cacheHeader;
00695 
00696         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
00697                                             cacheHeader);
00698         if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
00699           bMustRevalidate = PR_TRUE;
00700         }
00701       }
00702 
00703       if (bMustRevalidate) {
00704         mCacheEntry->SetMetaDataElement("MustValidateIfExpired", "true");
00705       }
00706     }
00707   }
00708 
00709 
00710   // Shouldn't we be dead already if this gets hit?  Probably multipart/x-mixed-replace...
00711   if (mObservers.Count() == 0) {
00712     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
00713   }
00714 
00715   return NS_OK;
00716 }
00717 
00718 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
00719 NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
00720 {
00721   LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
00722 
00723   mState |= onStopRequest;
00724 
00725   /* set our loading flag to false */
00726   mLoading = PR_FALSE;
00727 
00728   /* set our processing flag to false */
00729   mProcessing = PR_FALSE;
00730 
00731   mHadLastPart = PR_TRUE;
00732   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
00733   if (mpchan) {
00734     PRBool lastPart;
00735     nsresult rv = mpchan->GetIsLastPart(&lastPart);
00736     if (NS_SUCCEEDED(rv))
00737       mHadLastPart = lastPart;
00738   }
00739 
00740   // XXXldb What if this is a non-last part of a multipart request?
00741   if (mChannel) {
00742     mChannel->GetOriginalURI(getter_AddRefs(mURI));
00743     mChannel = nsnull; // we no longer need the channel
00744   }
00745 
00746   // If mImage is still null, we didn't properly load the image.
00747   if (NS_FAILED(status) || !mImage) {
00748     this->Cancel(status); // sets status, stops animations, removes from cache
00749   } else {
00750     mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
00751   }
00752 
00753   if (mDecoder) {
00754     mDecoder->Flush();
00755     mDecoder->Close();
00756     mDecoder = nsnull; // release the decoder so that it can rest peacefully ;)
00757   }
00758 
00759   // if there was an error loading the image, (mState & onStopDecode) won't be true.
00760   // Send an onStopDecode message
00761   if (!(mState & onStopDecode)) {
00762     this->OnStopDecode(nsnull, status, nsnull);
00763   }
00764 
00765   /* notify the kids */
00766   PRInt32 count = mObservers.Count();
00767   for (PRInt32 i = count-1; i>=0; i--) {
00768     imgRequestProxy *proxy = NS_STATIC_CAST(imgRequestProxy*, mObservers[i]);
00769     /* calling OnStopRequest may result in the death of |proxy| so don't use the
00770        pointer after this call.
00771      */
00772     if (proxy) proxy->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
00773   }
00774 
00775   return NS_OK;
00776 }
00777 
00778 
00779 
00780 
00781 /* prototype for this defined below */
00782 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
00783                                          PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
00784 
00785 
00788 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
00789 NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
00790 {
00791   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
00792 
00793   NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
00794 
00795   if (!mProcessing) {
00796     LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
00797 
00798     /* set our processing flag to true if this is the first OnDataAvailable() */
00799     mProcessing = PR_TRUE;
00800 
00801     /* look at the first few bytes and see if we can tell what the data is from that
00802      * since servers tend to lie. :(
00803      */
00804     PRUint32 out;
00805     inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
00806 
00807 #ifdef NS_DEBUG
00808     /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
00809 #endif
00810 
00811     if (mContentType.IsEmpty()) {
00812       LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
00813 
00814       nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
00815 
00816       nsresult rv = NS_ERROR_FAILURE;
00817       if (chan) {
00818         rv = chan->GetContentType(mContentType);
00819       }
00820 
00821       if (NS_FAILED(rv)) {
00822         PR_LOG(gImgLog, PR_LOG_ERROR,
00823                ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
00824                 this));
00825 
00826         this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
00827 
00828         return NS_BINDING_ABORTED;
00829       }
00830 
00831       LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
00832     }
00833 
00834     /* set our mimetype as a property */
00835     nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
00836     if (contentType) {
00837       contentType->SetData(mContentType);
00838       mProperties->Set("type", contentType);
00839     }
00840 
00841     /* set our content disposition as a property */
00842     nsCAutoString disposition;
00843     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
00844     if (httpChannel) {
00845       httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), disposition);
00846     } else {
00847       nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
00848       if (multiPartChannel) {
00849         multiPartChannel->GetContentDisposition(disposition);
00850       }
00851     }
00852     if (!disposition.IsEmpty()) {
00853       nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
00854       if (contentDisposition) {
00855         contentDisposition->SetData(disposition);
00856         mProperties->Set("content-disposition", contentDisposition);
00857       }
00858     }
00859 
00860     LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
00861 
00862     nsCAutoString conid(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mContentType);
00863 
00864     mDecoder = do_CreateInstance(conid.get());
00865 
00866     if (!mDecoder) {
00867       PR_LOG(gImgLog, PR_LOG_WARNING,
00868              ("[this=%p] imgRequest::OnDataAvailable -- Decoder not available\n", this));
00869 
00870       // no image decoder for this mimetype :(
00871       this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
00872 
00873       return NS_IMAGELIB_ERROR_NO_DECODER;
00874     }
00875 
00876     nsresult rv = mDecoder->Init(NS_STATIC_CAST(imgILoad*, this));
00877     if (NS_FAILED(rv)) {
00878       PR_LOG(gImgLog, PR_LOG_WARNING,
00879              ("[this=%p] imgRequest::OnDataAvailable -- mDecoder->Init failed\n", this));
00880 
00881       this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
00882 
00883       return NS_BINDING_ABORTED;
00884     }
00885   }
00886 
00887   if (!mDecoder) {
00888     PR_LOG(gImgLog, PR_LOG_WARNING,
00889            ("[this=%p] imgRequest::OnDataAvailable -- no decoder\n", this));
00890 
00891     this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
00892 
00893     return NS_BINDING_ABORTED;
00894   }
00895 
00896   PRUint32 wrote;
00897   nsresult rv = mDecoder->WriteFrom(inStr, count, &wrote);
00898 
00899   if (NS_FAILED(rv)) {
00900     PR_LOG(gImgLog, PR_LOG_WARNING,
00901            ("[this=%p] imgRequest::OnDataAvailable -- mDecoder->WriteFrom failed\n", this));
00902 
00903     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
00904 
00905     return NS_BINDING_ABORTED;
00906   }
00907 
00908   return NS_OK;
00909 }
00910 
00911 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
00912                                          void* closure,
00913                                          const char* fromRawSegment,
00914                                          PRUint32 toOffset,
00915                                          PRUint32 count,
00916                                          PRUint32 *writeCount)
00917 {
00918   imgRequest *request = NS_STATIC_CAST(imgRequest*, closure);
00919 
00920   NS_ASSERTION(request, "request is null!");
00921 
00922   if (count > 0)
00923     request->SniffMimeType(fromRawSegment, count);
00924 
00925   *writeCount = 0;
00926   return NS_ERROR_FAILURE;
00927 }
00928 
00929 void
00930 imgRequest::SniffMimeType(const char *buf, PRUint32 len)
00931 {
00932   imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
00933 }