392 lines
11 KiB
C++
392 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "nsHTMLImageLoader.h"
|
|
#include "nsIHTMLReflow.h"
|
|
#include "nsFrame.h"
|
|
#include "nsIURL.h"
|
|
#ifdef NECKO
|
|
#include "nsIIOService.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIServiceManager.h"
|
|
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
|
|
#endif // NECKO
|
|
|
|
#ifdef DEBUG
|
|
#undef NOISY_IMAGE_LOADING
|
|
#else
|
|
#undef NOISY_IMAGE_LOADING
|
|
#endif
|
|
|
|
nsHTMLImageLoader::nsHTMLImageLoader()
|
|
: mBaseURL(nsnull),
|
|
mFrame(nsnull),
|
|
mCallBack(nsnull),
|
|
mClosure(nsnull),
|
|
mImageLoader(nsnull),
|
|
mAllFlags(0),
|
|
mIntrinsicImageSize(0, 0),
|
|
mComputedImageSize(0, 0)
|
|
{
|
|
}
|
|
|
|
nsHTMLImageLoader::~nsHTMLImageLoader()
|
|
{
|
|
NS_IF_RELEASE(mBaseURL);
|
|
NS_IF_RELEASE(mImageLoader);
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::Init(nsIFrame* aFrame,
|
|
nsHTMLImageLoaderCB aCallBack,
|
|
void* aClosure,
|
|
nsIURL* aBaseURL,
|
|
const nsString& aURLSpec)
|
|
{
|
|
mFrame = aFrame;
|
|
mCallBack = aCallBack;
|
|
mClosure = aClosure;
|
|
mBaseURL = aBaseURL;
|
|
NS_IF_ADDREF(mBaseURL);
|
|
SetURL(aURLSpec);
|
|
}
|
|
|
|
nsIImage*
|
|
nsHTMLImageLoader::GetImage()
|
|
{
|
|
nsIImage* image = nsnull;
|
|
if (mImageLoader) {
|
|
mImageLoader->GetImage(&image);
|
|
}
|
|
return image;
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::SetURL(const nsString& aNewSpec)
|
|
{
|
|
mURLSpec = aNewSpec;
|
|
if (mBaseURL && !aNewSpec.Equals("")) {
|
|
nsString empty;
|
|
nsresult rv;
|
|
#ifndef NECKO
|
|
rv = NS_MakeAbsoluteURL(mBaseURL, empty, mURLSpec, mURL);
|
|
#else
|
|
NS_WITH_SERVICE(nsIIOService, service, kIOServiceCID, &rv);
|
|
if (NS_FAILED(rv)) return;
|
|
|
|
nsIURI *baseUri = nsnull;
|
|
rv = mBaseURL->QueryInterface(nsIURI::GetIID(), (void**)&baseUri);
|
|
if (NS_FAILED(rv)) return;
|
|
|
|
char *absUrl = nsnull;
|
|
const char *urlSpec = mURLSpec.GetBuffer();
|
|
rv = service->MakeAbsolute(urlSpec, baseUri, &absUrl);
|
|
NS_RELEASE(baseUri);
|
|
mURL = absUrl;
|
|
delete [] absUrl;
|
|
#endif // NECKO
|
|
if (NS_FAILED(rv)) {
|
|
mURL = mURLSpec;
|
|
}
|
|
} else {
|
|
mURL = mURLSpec;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::StopLoadImage(nsIPresContext* aPresContext)
|
|
{
|
|
if (mImageLoader) {
|
|
aPresContext->StopLoadImage(mFrame, mImageLoader);
|
|
NS_RELEASE(mImageLoader);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::StopAllLoadImages(nsIPresContext* aPresContext)
|
|
{
|
|
aPresContext->StopAllLoadImagesFor(mFrame);
|
|
}
|
|
|
|
nsresult
|
|
nsHTMLImageLoader::ImageLoadCB(nsIPresContext* aPresContext,
|
|
nsIFrameImageLoader* aLoader,
|
|
nsIFrame* aFrame,
|
|
void* aClosure,
|
|
PRUint32 aStatus)
|
|
{
|
|
if (aClosure) {
|
|
((nsHTMLImageLoader*)aClosure)->Update(aPresContext, aFrame, aStatus);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::Update(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
PRUint32 aStatus)
|
|
{
|
|
#ifdef NOISY_IMAGE_LOADING
|
|
nsFrame::ListTag(stdout, aFrame);
|
|
printf(": update: status=%x [loader=%p] callBack=%p squelch=%s\n",
|
|
aStatus, mImageLoader, mCallBack,
|
|
mFlags.mSquelchCallback ? "yes" : "no");
|
|
#endif
|
|
if (NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE & aStatus) {
|
|
if (mImageLoader) {
|
|
mImageLoader->GetSize(mIntrinsicImageSize);
|
|
if (mFlags.mNeedIntrinsicImageSize) {
|
|
mFlags.mHaveIntrinsicImageSize = PR_TRUE;
|
|
}
|
|
}
|
|
if ((NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE == aStatus) &&
|
|
!mFlags.mNeedSizeNotification) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Pass on update to the user of this object if they want it
|
|
if (mCallBack) {
|
|
if (!mFlags.mSquelchCallback) {
|
|
(*mCallBack)(aPresContext, this, aFrame, mClosure, aStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsHTMLImageLoader::StartLoadImage(nsIPresContext* aPresContext)
|
|
{
|
|
if (!mFrame) {
|
|
// We were not initialized!
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (mURL.Equals("")) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// This is kind of sick, but its possible that we will get a
|
|
// notification *before* we have setup mImageLoader. To get around
|
|
// this, we let the pres-context store into mImageLoader and sort
|
|
// things after it returns.
|
|
nsIFrameImageLoader* oldLoader = mImageLoader;
|
|
nsSize* sizeToLoadWidth = nsnull;
|
|
if (!mFlags.mAutoImageSize && !mFlags.mNeedIntrinsicImageSize) {
|
|
sizeToLoadWidth = &mComputedImageSize;
|
|
}
|
|
nsresult rv = aPresContext->StartLoadImage(mURL, nsnull,
|
|
sizeToLoadWidth,
|
|
mFrame, ImageLoadCB, (void*)this,
|
|
&mImageLoader);
|
|
#ifdef NOISY_IMAGE_LOADING
|
|
nsFrame::ListTag(stdout, mFrame);
|
|
printf(": loading image '");
|
|
fputs(mURL, stdout);
|
|
printf("' @ ");
|
|
if (mFlags.mNeedIntrinsicImageSize) {
|
|
printf("intrinsic size ");
|
|
}
|
|
printf("%d,%d; oldLoader=%p newLoader=%p\n",
|
|
mComputedImageSize.width, mComputedImageSize.height,
|
|
oldLoader, mImageLoader);
|
|
#endif
|
|
|
|
if (oldLoader != mImageLoader) {
|
|
if (nsnull != oldLoader) {
|
|
// Tell presentation context we are done with the old image loader
|
|
aPresContext->StopLoadImage(mFrame, oldLoader);
|
|
}
|
|
}
|
|
|
|
// Release the old image loader
|
|
NS_IF_RELEASE(oldLoader);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsHTMLImageLoader::UpdateURLSpec(nsIPresContext* aPresContext,
|
|
const nsString& aNewSpec)
|
|
{
|
|
SetURL(aNewSpec);
|
|
|
|
// Start image loading with the previously computed size information
|
|
StartLoadImage(aPresContext);
|
|
}
|
|
|
|
PRBool
|
|
nsHTMLImageLoader::GetDesiredSize(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState* aReflowState,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
nscoord widthConstraint = NS_INTRINSICSIZE;
|
|
nscoord heightConstraint = NS_INTRINSICSIZE;
|
|
PRBool fixedContentWidth = PR_FALSE;
|
|
PRBool fixedContentHeight = PR_FALSE;
|
|
|
|
if (aReflowState) {
|
|
widthConstraint = aReflowState->computedWidth;
|
|
heightConstraint = aReflowState->computedHeight;
|
|
|
|
// Determine whether the image has fixed content width and height
|
|
fixedContentWidth = aReflowState->HaveFixedContentWidth();
|
|
fixedContentHeight = aReflowState->HaveFixedContentHeight();
|
|
if (NS_INTRINSICSIZE == widthConstraint) {
|
|
fixedContentWidth = PR_FALSE;
|
|
}
|
|
if (NS_INTRINSICSIZE == heightConstraint) {
|
|
fixedContentHeight = PR_FALSE;
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
PRBool haveComputedSize = PR_FALSE;
|
|
PRBool needIntrinsicImageSize = PR_FALSE;
|
|
nscoord newWidth, newHeight;
|
|
mFlags.mAutoImageSize = PR_FALSE;
|
|
mFlags.mNeedSizeNotification = PR_FALSE;
|
|
if (fixedContentWidth) {
|
|
if (fixedContentHeight) {
|
|
newWidth = widthConstraint;
|
|
newHeight = heightConstraint;
|
|
haveComputedSize = PR_TRUE;
|
|
}
|
|
else {
|
|
// We have a width, and an auto height. Compute height from
|
|
// width once we have the intrinsic image size.
|
|
newWidth = widthConstraint;
|
|
if (mFlags.mHaveIntrinsicImageSize) {
|
|
float width = mIntrinsicImageSize.width
|
|
? mIntrinsicImageSize.width
|
|
: mIntrinsicImageSize.height; // avoid divide by zero
|
|
float height = mIntrinsicImageSize.height;
|
|
newHeight = (nscoord)
|
|
NSToIntRound(widthConstraint * height / width);
|
|
haveComputedSize = PR_TRUE;
|
|
}
|
|
else {
|
|
newHeight = 1;
|
|
needIntrinsicImageSize = PR_TRUE;
|
|
mFlags.mNeedSizeNotification = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if (fixedContentHeight) {
|
|
// We have a height, and an auto width. Compute width from height
|
|
// once we have the intrinsic image size.
|
|
newHeight = heightConstraint;
|
|
if (mFlags.mHaveIntrinsicImageSize) {
|
|
float width = mIntrinsicImageSize.width;
|
|
float height = mIntrinsicImageSize.height
|
|
? mIntrinsicImageSize.height
|
|
: mIntrinsicImageSize.width; // avoid divide by zero
|
|
newWidth = (nscoord)
|
|
NSToIntRound(heightConstraint * width / height);
|
|
haveComputedSize = PR_TRUE;
|
|
}
|
|
else {
|
|
newWidth = 1;
|
|
needIntrinsicImageSize = PR_TRUE;
|
|
mFlags.mNeedSizeNotification = PR_TRUE;
|
|
}
|
|
}
|
|
else {
|
|
mFlags.mAutoImageSize = PR_TRUE;
|
|
if (mFlags.mHaveIntrinsicImageSize) {
|
|
newWidth = mIntrinsicImageSize.width;
|
|
newHeight = mIntrinsicImageSize.height;
|
|
haveComputedSize = PR_TRUE;
|
|
}
|
|
else {
|
|
newWidth = 1;
|
|
newHeight = 1;
|
|
needIntrinsicImageSize = PR_TRUE;
|
|
mFlags.mNeedSizeNotification = PR_TRUE;
|
|
}
|
|
}
|
|
mFlags.mNeedIntrinsicImageSize = needIntrinsicImageSize;
|
|
mFlags.mHaveComputedSize = haveComputedSize;
|
|
mComputedImageSize.width = newWidth;
|
|
mComputedImageSize.height = newHeight;
|
|
#ifdef NOISY_IMAGE_LOADING
|
|
nsFrame::ListTag(stdout, mFrame);
|
|
printf(": %s%scomputedSize=%d,%d\n",
|
|
mFlags.mNeedIntrinsicImageSize ? "need-instrinsic-size " : "",
|
|
mFlags.mHaveComputedSize ? "have-computed-size " : "",
|
|
mComputedImageSize.width, mComputedImageSize.height);
|
|
#endif
|
|
|
|
// Load the image at the desired size
|
|
if ((0 != newWidth) && (0 != newHeight)) {
|
|
// Make sure we squelch a callback to the client of this image
|
|
// loader during a start-load-image. Its possible the image we
|
|
// want is ready to go and will therefore fire a notification
|
|
// during the StartLoadImage call. Since this routine is already
|
|
// returning size information there is no point in passing on the
|
|
// callbacks to the client.
|
|
mFlags.mSquelchCallback = PR_TRUE;
|
|
StartLoadImage(aPresContext);
|
|
mFlags.mSquelchCallback = PR_FALSE;
|
|
|
|
// See if we just got the intrinsic size
|
|
if (mFlags.mNeedIntrinsicImageSize && mFlags.mHaveIntrinsicImageSize) {
|
|
// We just learned our intrinisic size. Start over from the top...
|
|
#ifdef NOISY_IMAGE_LOADING
|
|
printf(" *** size arrived during StartLoadImage, looping...\n");
|
|
#endif
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
aDesiredSize.width = mComputedImageSize.width;
|
|
aDesiredSize.height = mComputedImageSize.height;
|
|
|
|
if ((mFlags.mNeedIntrinsicImageSize && !mFlags.mHaveIntrinsicImageSize) ||
|
|
mFlags.mNeedSizeNotification) {
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsHTMLImageLoader::GetLoadImageFailed() const
|
|
{
|
|
PRBool result = PR_FALSE;
|
|
|
|
if (nsnull != mImageLoader) {
|
|
// Ask the image loader whether the load failed
|
|
PRUint32 loadStatus;
|
|
mImageLoader->GetImageLoadStatus(&loadStatus);
|
|
result = 0 != (loadStatus & NS_IMAGE_LOAD_STATUS_ERROR);
|
|
return result;
|
|
}
|
|
|
|
result = mFlags.mLoadImageFailed ? PR_TRUE : PR_FALSE;
|
|
return result;
|
|
}
|
|
|
|
PRUint32
|
|
nsHTMLImageLoader::GetLoadStatus() const
|
|
{
|
|
PRUint32 loadStatus = NS_IMAGE_LOAD_STATUS_NONE;
|
|
if (mImageLoader) {
|
|
mImageLoader->GetImageLoadStatus(&loadStatus);
|
|
}
|
|
return loadStatus;
|
|
}
|