gecko-dev/modules/libpr0n/src/imgRequest.cpp

944 lines
28 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "imgRequest.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
2001-05-08 04:21:49 +00:00
#include "imgILoader.h"
2001-10-06 05:08:16 +00:00
#include "ImageErrors.h"
2001-05-08 04:21:49 +00:00
#include "ImageLogging.h"
#include "gfxIImageFrame.h"
#include "netCore.h"
#include "nsIChannel.h"
2001-05-08 04:21:49 +00:00
#include "nsICachingChannel.h"
2001-02-25 08:37:26 +00:00
#include "nsILoadGroup.h"
#include "nsIInputStream.h"
#include "nsIMultiPartChannel.h"
#include "nsIHttpChannel.h"
#include "nsIComponentManager.h"
2001-05-08 04:21:49 +00:00
#include "nsIProxyObjectManager.h"
2001-01-23 02:02:29 +00:00
#include "nsIServiceManager.h"
#include "nsISupportsPrimitives.h"
#include "nsString.h"
2001-03-14 00:39:48 +00:00
#include "nsXPIDLString.h"
#include "plstr.h" // PL_strcasestr(...)
#if defined(PR_LOGGING)
PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
#endif
NS_IMPL_ISUPPORTS6(imgRequest, imgILoad,
imgIDecoderObserver, imgIContainerObserver,
nsIStreamListener, nsIRequestObserver,
nsISupportsWeakReference)
imgRequest::imgRequest() :
mLoading(PR_FALSE), mProcessing(PR_FALSE), mHadLastPart(PR_FALSE),
mNetworkStatus(0), mImageStatus(imgIRequest::STATUS_NONE), mState(0),
mCacheId(0), mValidator(nsnull), mIsMultiPartChannel(PR_FALSE)
{
/* member initializers and constructor code */
}
imgRequest::~imgRequest()
{
/* destructor code */
}
nsresult imgRequest::Init(nsIURI *aURI,
nsIRequest *aRequest,
nsICacheEntryDescriptor *aCacheEntry,
void *aCacheId,
void *aLoadId)
{
2001-05-08 04:21:49 +00:00
LOG_FUNC(gImgLog, "imgRequest::Init");
NS_ASSERTION(!mImage, "Multiple calls to init");
NS_ASSERTION(aURI, "No uri");
NS_ASSERTION(aRequest, "No request");
mProperties = do_CreateInstance("@mozilla.org/properties;1");
if (!mProperties)
return NS_ERROR_OUT_OF_MEMORY;
mURI = aURI;
mRequest = aRequest;
/* set our loading flag to true here.
Setting it here lets checks to see if the load is in progress
before OnStartRequest gets called, letting 'this' properly get removed
from the cache in certain cases.
*/
mLoading = PR_TRUE;
mCacheEntry = aCacheEntry;
mCacheId = aCacheId;
SetLoadId(aLoadId);
2001-01-23 02:02:29 +00:00
return NS_OK;
}
nsresult imgRequest::AddProxy(imgRequestProxy *proxy, PRBool aNotify)
{
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
mObservers.AppendElement(static_cast<void*>(proxy));
if (aNotify)
NotifyProxyListener(proxy);
return NS_OK;
}
nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify)
{
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
mObservers.RemoveElement(static_cast<void*>(proxy));
/* Check mState below before we potentially call Cancel() below. Since
Cancel() may result in OnStopRequest being called back before Cancel()
returns, leaving mState in a different state then the one it was in at
this point.
*/
if (aNotify) {
// make sure that observer gets an OnStopDecode message sent to it
if (!(mState & onStopDecode)) {
proxy->OnStopDecode(aStatus, nsnull);
}
2002-01-16 03:23:50 +00:00
}
// make sure that observer gets an OnStopRequest message sent to it
if (!(mState & onStopRequest)) {
proxy->OnStopRequest(nsnull, nsnull, NS_BINDING_ABORTED, PR_TRUE);
}
if (mImage && !HaveProxyWithObserver(nsnull)) {
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation");
mImage->StopAnimation();
}
if (mObservers.Count() == 0) {
/* If |aStatus| is a failure code, then cancel the load if it is still in progress.
Otherwise, let the load continue, keeping 'this' in the cache with no observers.
This way, if a proxy is destroyed without calling cancel on it, it won't leak
and won't leave a bad pointer in mObservers.
*/
if (mRequest && mLoading && NS_FAILED(aStatus)) {
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling");
mImageStatus |= imgIRequest::STATUS_LOAD_PARTIAL;
this->Cancel(NS_BINDING_ABORTED);
}
/* break the cycle from the cache entry. */
mCacheEntry = nsnull;
}
// If a proxy is removed for a reason other than its owner being
// changed, remove the proxy from the loadgroup.
if (aStatus != NS_IMAGELIB_CHANGING_OWNER)
proxy->RemoveFromLoadGroup(PR_TRUE);
return NS_OK;
}
nsresult imgRequest::NotifyProxyListener(imgRequestProxy *proxy)
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
// OnStartRequest
if (mState & onStartRequest)
proxy->OnStartRequest(nsnull, nsnull);
2001-04-09 00:45:06 +00:00
// OnStartDecode
2001-02-20 22:43:56 +00:00
if (mState & onStartDecode)
proxy->OnStartDecode();
2001-04-09 00:45:06 +00:00
// OnStartContainer
2001-02-20 22:43:56 +00:00
if (mState & onStartContainer)
proxy->OnStartContainer(mImage);
2001-04-09 00:45:06 +00:00
// Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
2001-04-10 05:06:57 +00:00
PRUint32 nframes = 0;
if (mImage)
mImage->GetNumFrames(&nframes);
2001-04-09 00:45:06 +00:00
if (nframes > 0) {
nsCOMPtr<gfxIImageFrame> frame;
// get the current frame or only frame
mImage->GetCurrentFrame(getter_AddRefs(frame));
NS_ASSERTION(frame, "GetCurrentFrame gave back a null frame!");
2001-04-09 00:45:06 +00:00
// OnStartFrame
proxy->OnStartFrame(frame);
2001-04-09 00:45:06 +00:00
if (!(mState & onStopContainer)) {
// OnDataAvailable
nsIntRect r;
frame->GetRect(r); // XXX we should only send the currently decoded rectangle here.
proxy->OnDataAvailable(frame, &r);
2001-04-09 00:45:06 +00:00
} else {
// OnDataAvailable
nsIntRect r;
2001-04-09 00:45:06 +00:00
frame->GetRect(r); // We're done loading this image, send the the whole rect
proxy->OnDataAvailable(frame, &r);
2001-04-09 00:45:06 +00:00
// OnStopFrame
proxy->OnStopFrame(frame);
2001-04-09 00:45:06 +00:00
}
}
2001-04-09 00:45:06 +00:00
// OnStopContainer
2001-02-20 22:43:56 +00:00
if (mState & onStopContainer)
proxy->OnStopContainer(mImage);
2001-04-09 00:45:06 +00:00
// OnStopDecode
2001-02-20 22:43:56 +00:00
if (mState & onStopDecode)
proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation");
2001-02-23 23:48:08 +00:00
mImage->ResetAnimation();
2001-02-23 23:48:08 +00:00
}
if (mState & onStopRequest) {
proxy->OnStopRequest(nsnull, nsnull,
GetResultFromImageStatus(mImageStatus),
mHadLastPart);
}
2001-04-09 00:45:06 +00:00
2001-02-20 22:43:56 +00:00
return NS_OK;
}
nsresult imgRequest::GetResultFromImageStatus(PRUint32 aStatus) const
{
nsresult rv = NS_OK;
2001-05-08 04:21:49 +00:00
if (aStatus & imgIRequest::STATUS_ERROR)
rv = NS_IMAGELIB_ERROR_FAILURE;
2001-05-08 04:21:49 +00:00
else if (aStatus & imgIRequest::STATUS_LOAD_COMPLETE)
rv = NS_IMAGELIB_SUCCESS_LOAD_FINISHED;
return rv;
}
void imgRequest::Cancel(nsresult aStatus)
{
/* The Cancel() method here should only be called by this class. */
LOG_SCOPE(gImgLog, "imgRequest::Cancel");
2001-02-20 22:43:56 +00:00
2001-02-24 00:31:08 +00:00
if (mImage) {
2001-05-08 04:21:49 +00:00
LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
2001-02-24 00:31:08 +00:00
mImage->StopAnimation();
2001-02-24 00:31:08 +00:00
}
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus |= imgIRequest::STATUS_ERROR;
if (aStatus != NS_IMAGELIB_ERROR_NO_DECODER) {
RemoveFromCache();
}
if (mRequest && mLoading)
mRequest->Cancel(aStatus);
}
nsresult imgRequest::GetURI(nsIURI **aURI)
{
LOG_FUNC(gImgLog, "imgRequest::GetURI");
if (mURI) {
*aURI = mURI;
NS_ADDREF(*aURI);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
void imgRequest::RemoveFromCache()
{
LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
if (mCacheEntry) {
mCacheEntry->Doom();
mCacheEntry = nsnull;
}
}
PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
{
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy == aProxyToIgnore) {
continue;
}
if (proxy->HasObserver()) {
return PR_TRUE;
}
}
return PR_FALSE;
}
PRInt32 imgRequest::Priority() const
{
PRInt32 priority = nsISupportsPriority::PRIORITY_NORMAL;
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
if (p)
p->GetPriority(&priority);
return priority;
}
void imgRequest::AdjustPriority(imgRequestProxy *proxy, PRInt32 delta)
{
// only the first proxy is allowed to modify the priority of this image load.
//
// XXX(darin): this is probably not the most optimal algorithm as we may want
// to increase the priority of requests that have a lot of proxies. the key
// concern though is that image loads remain lower priority than other pieces
// of content such as link clicks, CSS, and JS.
//
if (mObservers[0] != proxy)
return;
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
if (p)
p->AdjustPriority(delta);
}
/** imgILoad methods **/
NS_IMETHODIMP imgRequest::SetImage(imgIContainer *aImage)
{
2001-05-08 04:21:49 +00:00
LOG_FUNC(gImgLog, "imgRequest::SetImage");
mImage = aImage;
2001-02-20 22:43:56 +00:00
return NS_OK;
}
NS_IMETHODIMP imgRequest::GetImage(imgIContainer **aImage)
{
LOG_FUNC(gImgLog, "imgRequest::GetImage");
*aImage = mImage;
NS_IF_ADDREF(*aImage);
return NS_OK;
}
NS_IMETHODIMP imgRequest::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel)
{
LOG_FUNC(gImgLog, "imgRequest::GetIsMultiPartChannel");
*aIsMultiPartChannel = mIsMultiPartChannel;
return NS_OK;
}
2001-03-10 01:11:54 +00:00
/** imgIContainerObserver methods **/
/* [noscript] void frameChanged (in imgIContainer container, in gfxIImageFrame newframe, in nsIntRect dirtyRect); */
NS_IMETHODIMP imgRequest::FrameChanged(imgIContainer *container,
gfxIImageFrame *newframe,
nsIntRect * dirtyRect)
2001-02-20 22:43:56 +00:00
{
LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
2001-02-20 22:43:56 +00:00
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->FrameChanged(container, newframe, dirtyRect);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
2001-02-20 23:45:51 +00:00
/** imgIDecoderObserver methods **/
/* void onStartDecode (in imgIRequest request); */
NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
2001-02-20 22:43:56 +00:00
mState |= onStartDecode;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStartDecode();
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
/* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
indicates the beginning of a new decode.
The cache entry's size therefore needs to be reset to 0 here. If we do not do this,
the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
*/
if (mCacheEntry)
mCacheEntry->SetDataSize(0);
return NS_OK;
}
NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest)
{
NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
return NS_OK;
}
/* void onStartContainer (in imgIRequest request, in imgIContainer image); */
NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer");
2001-05-01 02:53:27 +00:00
NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
if (!image) return NS_ERROR_UNEXPECTED;
2001-02-20 22:43:56 +00:00
mState |= onStartContainer;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
2001-02-20 22:43:56 +00:00
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStartContainer(image);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
/* void onStartFrame (in imgIRequest request, in gfxIImageFrame frame); */
NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
gfxIImageFrame *frame)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
2001-02-20 22:43:56 +00:00
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStartFrame(frame);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
/* [noscript] void onDataAvailable (in imgIRequest request, in gfxIImageFrame frame, [const] in nsIntRect rect); */
NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
gfxIImageFrame *frame,
const nsIntRect * rect)
{
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
2001-02-20 22:43:56 +00:00
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnDataAvailable(frame, rect);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
/* void onStopFrame (in imgIRequest request, in gfxIImageFrame frame); */
NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
gfxIImageFrame *frame)
{
NS_ASSERTION(frame, "imgRequest::OnStopFrame called with NULL frame");
if (!frame) return NS_ERROR_UNEXPECTED;
LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
if (mCacheEntry) {
PRUint32 cacheSize = 0;
mCacheEntry->GetDataSize(&cacheSize);
PRUint32 imageSize = 0;
frame->GetImageDataLength(&imageSize);
mCacheEntry->SetDataSize(cacheSize + imageSize);
}
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStopFrame(frame);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
/* void onStopContainer (in imgIRequest request, in imgIContainer image); */
NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
imgIContainer *image)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
2001-02-20 22:43:56 +00:00
mState |= onStopContainer;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStopContainer(image);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
/* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode");
NS_ASSERTION(!(mState & onStopDecode), "OnStopDecode called multiple times.");
2001-04-09 00:45:06 +00:00
2001-02-20 22:43:56 +00:00
mState |= onStopDecode;
if (NS_FAILED(aStatus) && !(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) {
mImageStatus |= imgIRequest::STATUS_ERROR;
2001-08-21 20:17:22 +00:00
}
2001-02-20 22:43:56 +00:00
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), aStatusArg);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
2001-02-20 22:43:56 +00:00
}
return NS_OK;
}
NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
PRBool aLastPart)
{
NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
return NS_OK;
}
/** nsIRequestObserver methods **/
2001-02-22 08:41:20 +00:00
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
{
nsresult rv;
LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
NS_ASSERTION(!mDecoder, "imgRequest::OnStartRequest -- we already have a decoder");
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan)
mIsMultiPartChannel = PR_TRUE;
/* set our state variables to their initial values, but advance mState
to onStartRequest. */
mImageStatus = imgIRequest::STATUS_NONE;
mState = onStartRequest;
2001-03-14 00:39:48 +00:00
/* set our loading flag to true */
mLoading = PR_TRUE;
/* notify our kids */
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; i++) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
if (proxy) proxy->OnStartRequest(aRequest, ctxt);
// If this assertion fires, it means that imgRequest notifications could
// be dropped!
NS_ASSERTION(count == mObservers.Count(),
"The observer list changed while being iterated over!");
}
2001-02-22 08:41:20 +00:00
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
/* get the expires info */
if (mCacheEntry) {
nsCOMPtr<nsICachingChannel> cacheChannel(do_QueryInterface(aRequest));
if (cacheChannel) {
nsCOMPtr<nsISupports> cacheToken;
cacheChannel->GetCacheToken(getter_AddRefs(cacheToken));
if (cacheToken) {
nsCOMPtr<nsICacheEntryInfo> entryDesc(do_QueryInterface(cacheToken));
if (entryDesc) {
2001-03-19 21:57:39 +00:00
PRUint32 expiration;
/* get the expiration time from the caching channel's token */
entryDesc->GetExpirationTime(&expiration);
/* set the expiration time on our entry */
mCacheEntry->SetExpirationTime(expiration);
}
}
}
//
// Determine whether the cache entry must be revalidated when it expires.
// If so, then the cache entry must *not* be used during HISTORY loads if
// it has expired.
//
// Currently, only HTTP specifies this information...
//
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
if (httpChannel) {
PRBool bMustRevalidate = PR_FALSE;
rv = httpChannel->IsNoStoreResponse(&bMustRevalidate);
if (!bMustRevalidate) {
rv = httpChannel->IsNoCacheResponse(&bMustRevalidate);
}
if (!bMustRevalidate) {
nsCAutoString cacheHeader;
rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
cacheHeader);
if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
bMustRevalidate = PR_TRUE;
}
}
if (bMustRevalidate) {
mCacheEntry->SetMetaDataElement("MustValidateIfExpired", "true");
}
}
2001-02-24 23:45:30 +00:00
}
// Shouldn't we be dead already if this gets hit? Probably multipart/x-mixed-replace...
if (mObservers.Count() == 0) {
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
}
return NS_OK;
}
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
{
2001-05-08 04:21:49 +00:00
LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
2001-02-20 22:43:56 +00:00
mState |= onStopRequest;
2001-03-14 00:39:48 +00:00
/* set our loading flag to false */
mLoading = PR_FALSE;
/* set our processing flag to false */
mProcessing = PR_FALSE;
2001-02-20 22:43:56 +00:00
mHadLastPart = PR_TRUE;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan) {
PRBool lastPart;
nsresult rv = mpchan->GetIsLastPart(&lastPart);
if (NS_SUCCEEDED(rv))
mHadLastPart = lastPart;
}
// XXXldb What if this is a non-last part of a multipart request?
// xxx before we release our reference to mChannel, lets
// save the last status that we saw so that the
// imgRequestProxy will have access to it.
if (mRequest)
{
mRequest->GetStatus(&mNetworkStatus);
mRequest = nsnull; // we no longer need the request
}
2001-05-01 02:53:27 +00:00
// If mImage is still null, we didn't properly load the image.
if (NS_FAILED(status) || !mImage) {
this->Cancel(status); // sets status, stops animations, removes from cache
2001-04-09 00:45:06 +00:00
} else {
mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
}
if (mDecoder) {
mDecoder->Flush();
mDecoder->Close();
mDecoder = nsnull; // release the decoder so that it can rest peacefully ;)
}
2001-04-09 00:45:06 +00:00
// if there was an error loading the image, (mState & onStopDecode) won't be true.
// Send an onStopDecode message
if (!(mState & onStopDecode)) {
this->OnStopDecode(nsnull, status, nsnull);
2001-04-09 00:45:06 +00:00
}
/* notify the kids */
PRInt32 count = mObservers.Count();
for (PRInt32 i = count-1; i>=0; i--) {
imgRequestProxy *proxy = static_cast<imgRequestProxy*>(mObservers[i]);
/* calling OnStopRequest may result in the death of |proxy| so don't use the
pointer after this call.
*/
if (proxy) proxy->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
}
2001-02-20 22:43:56 +00:00
return NS_OK;
}
2001-03-14 00:39:48 +00:00
/* prototype for this defined below */
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
2001-03-14 00:39:48 +00:00
/** nsIStreamListener methods **/
2001-02-22 08:41:20 +00:00
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
{
2001-05-08 04:21:49 +00:00
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
2001-03-14 00:39:48 +00:00
if (!mProcessing) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
2001-03-14 00:39:48 +00:00
/* set our processing flag to true if this is the first OnDataAvailable() */
mProcessing = PR_TRUE;
/* look at the first few bytes and see if we can tell what the data is from that
* since servers tend to lie. :(
*/
PRUint32 out;
inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
#ifdef NS_DEBUG
/* NS_WARNING if the content type from the channel isn't the same if the sniffing */
#endif
if (mContentType.IsEmpty()) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
nsresult rv = NS_ERROR_FAILURE;
if (chan) {
rv = chan->GetContentType(mContentType);
}
2001-03-14 00:39:48 +00:00
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_ERROR,
("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
2001-03-14 00:39:48 +00:00
this));
2001-08-21 20:17:22 +00:00
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
2001-03-14 00:39:48 +00:00
return NS_BINDING_ABORTED;
2001-03-14 00:39:48 +00:00
}
2001-05-08 04:21:49 +00:00
LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
2001-03-14 00:39:48 +00:00
}
/* set our mimetype as a property */
nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
if (contentType) {
contentType->SetData(mContentType);
mProperties->Set("type", contentType);
}
/* set our content disposition as a property */
nsCAutoString disposition;
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
if (httpChannel) {
httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-disposition"), disposition);
} else {
nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
if (multiPartChannel) {
multiPartChannel->GetContentDisposition(disposition);
}
}
if (!disposition.IsEmpty()) {
nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
if (contentDisposition) {
contentDisposition->SetData(disposition);
mProperties->Set("content-disposition", contentDisposition);
}
}
LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
2001-03-14 00:39:48 +00:00
nsCAutoString conid(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + mContentType);
2001-03-14 00:39:48 +00:00
mDecoder = do_CreateInstance(conid.get());
2001-03-14 00:39:48 +00:00
if (!mDecoder) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- Decoder not available\n", this));
2001-03-14 00:39:48 +00:00
// no image decoder for this mimetype :(
2001-08-21 20:17:22 +00:00
this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
2001-03-14 00:39:48 +00:00
2001-04-09 00:45:06 +00:00
return NS_IMAGELIB_ERROR_NO_DECODER;
2001-03-14 00:39:48 +00:00
}
nsresult rv = mDecoder->Init(static_cast<imgILoad*>(this));
2001-04-30 23:00:02 +00:00
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- mDecoder->Init failed\n", this));
2001-08-21 20:17:22 +00:00
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
2001-04-30 23:00:02 +00:00
return NS_BINDING_ABORTED;
}
2001-03-14 00:39:48 +00:00
}
2001-02-20 22:43:56 +00:00
if (!mDecoder) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- no decoder\n", this));
2001-02-25 09:02:25 +00:00
2001-08-21 20:17:22 +00:00
this->Cancel(NS_IMAGELIB_ERROR_NO_DECODER);
return NS_BINDING_ABORTED;
2001-02-20 22:43:56 +00:00
}
PRUint32 wrote;
nsresult rv = mDecoder->WriteFrom(inStr, count, &wrote);
if (NS_FAILED(rv)) {
PR_LOG(gImgLog, PR_LOG_WARNING,
("[this=%p] imgRequest::OnDataAvailable -- mDecoder->WriteFrom failed\n", this));
2001-08-21 20:17:22 +00:00
this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
return NS_BINDING_ABORTED;
}
return NS_OK;
}
2001-03-14 00:39:48 +00:00
static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount)
{
imgRequest *request = static_cast<imgRequest*>(closure);
2001-03-14 00:39:48 +00:00
NS_ASSERTION(request, "request is null!");
if (count > 0)
request->SniffMimeType(fromRawSegment, count);
*writeCount = 0;
return NS_ERROR_FAILURE;
}
void
imgRequest::SniffMimeType(const char *buf, PRUint32 len)
{
imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
}
nsresult
imgRequest::GetNetworkStatus()
{
nsresult status;
if (mRequest)
mRequest->GetStatus(&status);
else
status = mNetworkStatus;
return status;
}