mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1555 lines
45 KiB
1555 lines
45 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Jim Seidman [email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
#include "safestrm.h"
|
|
#include "decoder.h"
|
|
#include "blob.h"
|
|
|
|
// used to implement bNoImageCache - count of Image_Fetch_Async calls
|
|
static unsigned long gcbImgLoadCount = 1;
|
|
|
|
static far struct hash_table gImageCache;
|
|
static unsigned int gTotalCached = 0;
|
|
static unsigned int gTotalAvailable = 0;
|
|
|
|
static BOOL Image_Nuke(struct _www *w3doc,int cbElement,BOOL bNukeMaps, BOOL bNukeDCache);
|
|
|
|
static void Image_Swap(struct _www *w3doc,struct ImageInfo *pOld,struct ImageInfo *pNew);
|
|
static void Image_Revert(struct ImageInfo *pOld);
|
|
|
|
|
|
|
|
static void x_DisposeImage(struct ImageInfo *img, BOOL bFreeImg)
|
|
{
|
|
if (img->hPalette)
|
|
DeleteObject(img->hPalette);
|
|
if (img->data)
|
|
{
|
|
int cbBytesPer = ((wg.eColorMode != 8) && (wg.eColorMode != 4) && img->flags & IMG_JPEG) ? 3 : 1;
|
|
gTotalCached -= img->width*img->height*cbBytesPer;
|
|
GTR_FREE(img->data);
|
|
}
|
|
if (img->pbmi)
|
|
{
|
|
GTR_FREE(img->pbmi);
|
|
}
|
|
if (img->srcURL && bFreeImg)
|
|
{
|
|
GTR_FREE(img->srcURL);
|
|
}
|
|
if (img->actualURL && img->actualURL != img->srcURL)
|
|
{
|
|
GTR_FREE(img->actualURL);
|
|
}
|
|
if (bFreeImg)
|
|
{
|
|
if (img->pImgOtherVers) img->pImgOtherVers->pImgOtherVers = NULL;
|
|
GTR_FREE(img);
|
|
}
|
|
else
|
|
{
|
|
img->hPalette = NULL;
|
|
img->data = NULL;
|
|
img->pbmi = NULL;
|
|
img->actualURL = img->srcURL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Reduce the amount of memory used by images. If nWanted < 0, gTotalCached
|
|
* is ignored and all possible memory is freed, otherwise nWanted is ignored.
|
|
* BOOL fNoW3Docs: if TRUE, then we free only the images that currently
|
|
* have a zero ref. count (used by dcache code)
|
|
* if FALSE, then we are allowed to free w3doc's and then
|
|
* free more images that will have a zero refcount as a
|
|
* result of the freeing up of w3docs.
|
|
*
|
|
*/
|
|
extern BOOL Image_ReduceMemory(int nWanted, BOOL fOKToDelW3Docs)
|
|
{
|
|
struct ImageInfo *img;
|
|
int ndx;
|
|
int count;
|
|
BOOL bStillTrying = TRUE;
|
|
BOOL bDidFree = FALSE;
|
|
DWORD totalAvailable = (nWanted < 0 ? 0 : gTotalAvailable);
|
|
|
|
while (bStillTrying)
|
|
{
|
|
if (gTotalCached > totalAvailable)
|
|
{
|
|
count = Hash_Count(&gImageCache);
|
|
for (ndx = count-1; ndx >= 0; ndx--)
|
|
{
|
|
Hash_GetIndexedEntry(&gImageCache, ndx, NULL, NULL, (void **) &img);
|
|
if (!img->refCount)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Deleting entry %d from the image cache\n", ndx));
|
|
/* If this is merely a placeholder, there may not be any image to delete */
|
|
x_DisposeImage(img, TRUE);
|
|
Hash_DeleteIndexedEntry(&gImageCache, ndx);
|
|
bDidFree = TRUE;
|
|
if (gTotalCached > totalAvailable)
|
|
{
|
|
bStillTrying = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
bStillTrying = ( fOKToDelW3Docs
|
|
&& W3Doc_ReduceMemory(1, NULL));
|
|
} else bStillTrying = FALSE;
|
|
}
|
|
#ifdef XX_DEBUG
|
|
if (gTotalCached >= gTotalAvailable)
|
|
{
|
|
/* The cache is now overfull */
|
|
XX_DMsg(DBG_IMAGE, ("Need to delete an image from the cache, but cannot\n"));
|
|
}
|
|
#endif
|
|
return bDidFree;
|
|
}
|
|
|
|
static void Image_InitHash(void)
|
|
{
|
|
static BOOL bInitialized = FALSE;
|
|
MEMORYSTATUS memStatus;
|
|
int denominator;
|
|
|
|
if (!bInitialized)
|
|
{
|
|
Hash_Init(&gImageCache);
|
|
#ifdef USE_MEMMANAGE
|
|
/* We give it a small initial value but a big increment, since after
|
|
the first call we won't be able to free any more unless some
|
|
documents get freed too. */
|
|
GTR_RegisterMemoryReducer(Image_ReduceMemory, 1.0f, 10.0f, TRUE);
|
|
#endif
|
|
// guard against rediculously large cache fraction
|
|
denominator = gPrefs.image_cache_size;
|
|
if (denominator < 4) denominator = 4;
|
|
bInitialized = TRUE;
|
|
memStatus.dwLength = sizeof(MEMORYSTATUS);
|
|
GlobalMemoryStatus(&memStatus);
|
|
gTotalAvailable = memStatus.dwTotalPhys / denominator;
|
|
if (gTotalAvailable + MINIMUM_DELTAPRINT > memStatus.dwAvailPageFile)
|
|
{
|
|
gTotalAvailable = memStatus.dwAvailPageFile < MINIMUM_DELTAPRINT ? 0 : memStatus.dwAvailPageFile - MINIMUM_DELTAPRINT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int Image_AddToCache(const char *url, struct ImageInfo *myImage)
|
|
{
|
|
return Hash_Add(&gImageCache, url, NULL, (void *) myImage);
|
|
}
|
|
|
|
void Image_NukeRef(struct ImageInfo *pImg)
|
|
{
|
|
struct ImageInfo *pHashImg;
|
|
|
|
pImg->refCount--;
|
|
if (pImg->refCount == 0)
|
|
{
|
|
Hash_Find(&gImageCache, pImg->srcURL, NULL, (void **) &pHashImg);
|
|
if (pHashImg != pImg) // Is it a stale version?
|
|
{
|
|
// DEEPAK: here you blast pImg from aux cache
|
|
/* What if it isn't in the aux cache? Is it expected to be there?
|
|
* I saw a case where it wasn't in the aux cache
|
|
*/
|
|
DeleteAuxEntry(pImg);
|
|
XX_DMsg(DBG_IMAGE, ("freeing stale image %s\n",pImg->srcURL));
|
|
x_DisposeImage(pImg, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Image_AddRef(struct ImageInfo *pImg)
|
|
{
|
|
pImg->refCount++;
|
|
}
|
|
|
|
/*
|
|
The HTGIF and HTXBM classes call this function when a complete image
|
|
has been received.
|
|
*/
|
|
void Image_SetImageData(const char *srcURL, const char *actualURL, unsigned char *data, int width, int height, HPALETTE hPalette, long transparent, unsigned int flags,unsigned long cbCheckSum)
|
|
{
|
|
struct ImageInfo *pImg;
|
|
|
|
XX_DMsg(DBG_IMAGE, ("Image_SetImageData: Called for %s (%s) (w = %d, h =%d)\n", srcURL,(actualURL ? actualURL:""), width, height));
|
|
|
|
/* Find the entry for this image in the cache */
|
|
pImg = NULL;
|
|
Hash_Find(&gImageCache, srcURL, NULL, (void **) &pImg);
|
|
if (!pImg)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Image_SetImageData: No image placeholder exists for '%s'\n", srcURL));
|
|
return;
|
|
}
|
|
|
|
#ifdef XX_DEBUG
|
|
/* See whether the image's actual width and height match the placeholder's */
|
|
if (pImg->width && (pImg->width != width || pImg->height != height))
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Image_SetImageData: size mismatch for '%s'\n\t\told size = (%d,%d), new = (%d,%d)\n",
|
|
srcURL, pImg->width, pImg->height, width, height));
|
|
}
|
|
#endif
|
|
|
|
x_DisposeImage(pImg, FALSE);
|
|
|
|
|
|
pImg->cbCheckSum = cbCheckSum;
|
|
|
|
if (width == 0)
|
|
{
|
|
/* OK, this is a hack where height now contains error flags. */
|
|
if (height)
|
|
pImg->flags = height;
|
|
else
|
|
pImg->flags = IMG_ERROR;
|
|
|
|
pImg->width = 0;
|
|
pImg->height = 0;
|
|
#ifdef WIN32
|
|
pImg->data = NULL;
|
|
pImg->hPalette = NULL;
|
|
pImg->transparent = -1;
|
|
#endif
|
|
#ifdef MAC
|
|
pImg->gw = NULL;
|
|
#endif
|
|
#ifdef UNIX
|
|
pImg->data = NULL;
|
|
pImg->xPalette = NULL;
|
|
pImg->depth = 0;
|
|
pImg->xpix = 0;
|
|
pImg->transparent = -1;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pImg->width = width;
|
|
pImg->height = height;
|
|
#ifdef WIN32
|
|
if (data)
|
|
{
|
|
int cbBytesPer = ((wg.eColorMode != 8) && (wg.eColorMode != 4) && flags & IMG_JPEG) ? 3 : 1;
|
|
gTotalCached += width*height*cbBytesPer;
|
|
}
|
|
|
|
pImg->data = data;
|
|
pImg->hPalette = hPalette;
|
|
pImg->transparent = transparent;
|
|
pImg->flags = flags;
|
|
Image_ReduceMemory(0, /*fOKToDelW3Docs=*/TRUE);
|
|
XX_DMsg(DBG_IMAGE, ("total cached = %d\n",gTotalCached));
|
|
#endif
|
|
#ifdef MAC
|
|
pImg->gw = gw;
|
|
pImg->flags = 0;
|
|
#endif
|
|
#ifdef UNIX
|
|
pImg->data = data;
|
|
pImg->xPalette = xPalette;
|
|
pImg->depth = depth;
|
|
pImg->transparent = transparent;
|
|
pImg->flags = 0;
|
|
#endif
|
|
}
|
|
if (actualURL && strcmp(actualURL,srcURL)) pImg->actualURL = GTR_strdup(actualURL);
|
|
}
|
|
|
|
void *Image_GetDecoder(const char *srcURL)
|
|
{
|
|
struct ImageInfo *pImg;
|
|
|
|
/* Find the entry for this image in the cache */
|
|
pImg = NULL;
|
|
Hash_Find(&gImageCache, srcURL, NULL, (void **) &pImg);
|
|
return pImg ? pImg->decoderObject:NULL;
|
|
}
|
|
|
|
struct ImageInfo *Image_CreatePlaceholder(const char *full_address, int width, int height)
|
|
{
|
|
struct ImageInfo *myImage;
|
|
|
|
Image_InitHash();
|
|
|
|
/*
|
|
Check to see if it's in the cache
|
|
*/
|
|
myImage = NULL;
|
|
if (full_address) Hash_Find(&gImageCache, full_address, NULL, (void **) &myImage);
|
|
if (myImage)
|
|
{
|
|
if (width && height)
|
|
{
|
|
if (!myImage->width)
|
|
{
|
|
myImage->width = width;
|
|
myImage->height = height;
|
|
}
|
|
#ifdef XX_DEBUG
|
|
else
|
|
{
|
|
if (myImage->width != width)
|
|
XX_DMsg(DBG_IMAGE, ("Width mismatch on %s: hint=%d, previous=%d\n",myImage->srcURL, width, myImage->width));
|
|
if (myImage->height != height)
|
|
XX_DMsg(DBG_IMAGE, ("Height mismatch on %s: hint=%d, previous=%d\n", myImage->srcURL, height, myImage->height));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
myImage = (struct ImageInfo *) GTR_MALLOC(sizeof(struct ImageInfo));
|
|
memset(myImage, 0, sizeof(struct ImageInfo));
|
|
myImage->flags = IMG_NOTLOADED;
|
|
if (!gPrefs.bAutoLoadImages)
|
|
myImage->flags |= IMG_LOADSUP;
|
|
myImage->width = width;
|
|
myImage->height = height;
|
|
if (width && height) myImage->flags |= IMG_WHKNOWN;
|
|
if (full_address){
|
|
Image_AddToCache(full_address, myImage);
|
|
myImage->srcURL = GTR_strdup(full_address);
|
|
}
|
|
myImage->actualURL = myImage->srcURL;
|
|
return myImage;
|
|
}
|
|
return myImage;
|
|
}
|
|
|
|
|
|
// FilterProc for the unblock conditionally on master blocked for reap
|
|
static boolean MasterFilter(ThreadID theThread,void *context)
|
|
{
|
|
struct Mwin *tw = Async_GetWindowFromThread(theThread);
|
|
|
|
if (tw == context)
|
|
{
|
|
if (TW_GETBLOCKED(tw,TW_REAPBLOCKED))
|
|
{
|
|
TW_CLEARBLOCKED(tw,TW_REAPBLOCKED);
|
|
return TRUE;
|
|
}
|
|
else if (TW_GETBLOCKED(tw,TW_VISBLOCKED))
|
|
{
|
|
TW_CLEARBLOCKED(tw,TW_VISBLOCKED);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Does a free of a ref counted object, suitable for sharing between async
|
|
threads. object is only freed when last accessor frees.
|
|
*/
|
|
void *pSafeFree(void *pSafeObject)
|
|
{
|
|
PSAFERESULT pResult = pSafeObject;
|
|
|
|
if (pResult != NULL)
|
|
{
|
|
if (--pResult->refcnt < 0)
|
|
{
|
|
GTR_FREE(pResult);
|
|
pResult = NULL;
|
|
}
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
|
|
/* returns TRUE iff there exists another accessor besides caller.
|
|
*/
|
|
INLINE BOOL bExistsAnother(void *pSafeObject)
|
|
{
|
|
PSAFERESULT pResult = pSafeObject;
|
|
|
|
return (pResult == NULL || pResult->refcnt == 0) ? FALSE : TRUE;
|
|
}
|
|
|
|
static void Nuke_Request(HTRequest *request)
|
|
{
|
|
request->referer = NULL;
|
|
|
|
if (request->szLocalFileName)
|
|
{
|
|
GTR_FREE(request->szLocalFileName);
|
|
request->szLocalFileName = NULL;
|
|
}
|
|
Dest_DestroyDest(request->destination);
|
|
request->destination = NULL;
|
|
HTRequest_delete(request);
|
|
}
|
|
|
|
INLINE struct ImageInfo *pImgLookup(struct Mwin *twDoc,int nEl)
|
|
{
|
|
return (twDoc->w3doc == NULL ? NULL : twDoc->w3doc->aElements[nEl].myImage);
|
|
}
|
|
|
|
struct Params_Image_Load
|
|
{
|
|
HTRequest * request; /* Request to use to load image */
|
|
PSAFERESULT pStatus; /* used to pass back status across threads */
|
|
void *decoderObject; /* Decoder reserved for fetch */
|
|
unsigned long cbRequestID; /* uniquely identifies operation & decoder */
|
|
struct Mwin *twDoc; /* window defining w3doc w/ img */
|
|
int nEl; /* Which element we're on */
|
|
ThreadID parentThread; /* thread id of parent to unblock */
|
|
};
|
|
|
|
#define STATE_LOAD_TRIEDLOAD (STATE_OTHER)
|
|
int Image_Load_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_Image_Load *pParams;
|
|
struct Params_LoadAsync *pLoadParams;
|
|
struct ImageInfo *pImg;
|
|
|
|
pParams = *ppInfo;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
pParams->request = HTRequest_validate(pParams->request);
|
|
if (pParams->request == NULL || tw == NULL) return STATE_ABORT;
|
|
|
|
pImg = pImgLookup(pParams->twDoc,pParams->nEl);
|
|
XX_DMsg(DBG_IMAGE, ("Image_Load_Async: Called for %s\n", pImg ?pImg->srcURL:"DEAD IMAGE"));
|
|
|
|
{
|
|
pLoadParams = GTR_MALLOC(sizeof(*pLoadParams));
|
|
if (pLoadParams == NULL) return STATE_ABORT;
|
|
|
|
pLoadParams->request = pParams->request;
|
|
pLoadParams->pStatus = &(pParams->pStatus->status);
|
|
pLoadParams->fLoadFromDCacheOK = FALSE;
|
|
Async_DoCall(HTLoadDocument_Async, pLoadParams);
|
|
}
|
|
return STATE_LOAD_TRIEDLOAD;
|
|
|
|
case STATE_LOAD_TRIEDLOAD:
|
|
case STATE_ABORT:
|
|
/* Need to deal with possibility that decoder has been shot out from under */
|
|
pParams->request = HTRequest_validate(pParams->request);
|
|
if (tw)
|
|
{
|
|
pImg = pImgLookup(pParams->twDoc,pParams->nEl);
|
|
if (pImg &&
|
|
pParams->decoderObject == pImg->decoderObject &&
|
|
cbDC_GetRequestID(pParams->decoderObject) == pParams->cbRequestID &&
|
|
cbDC_GetStatus(pParams->decoderObject) == DC_Reserved)
|
|
{
|
|
DC_Abort(pParams->decoderObject);
|
|
pImg->decoderObject = NULL;
|
|
pImg->flags &= ~IMG_LOADING;
|
|
}
|
|
}
|
|
pParams->pStatus = pSafeFree(pParams->pStatus);
|
|
if (pParams->pStatus == NULL && pParams->request)
|
|
{
|
|
Nuke_Request(pParams->request);
|
|
XX_DMsg(DBG_IMAGE, ("dangling pointer in ImageLoad\n"));
|
|
}
|
|
else Async_UnblockThread(pParams->parentThread);
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
}
|
|
|
|
void DoUpdateImg(struct Params_Image_Fetch *pParams,BOOL bIsComplete)
|
|
{
|
|
struct _www *w3doc = pParams->twDoc->w3doc;
|
|
struct _element *aElements = w3doc->aElements;
|
|
int i;
|
|
long oldYbound = 0;
|
|
struct ImageInfo *myImage;
|
|
struct ImageInfo *target = aElements[pParams->nEl].myImage;
|
|
|
|
/* perhaps an img hint lied - shame,shame */
|
|
|
|
(void) W3Doc_CheckForImageLoad(w3doc);
|
|
|
|
/* reformat will redraw everything past the last formatted line */
|
|
|
|
if (w3doc->frame.nLastFormattedLine > 0)
|
|
oldYbound = w3doc->ybound;
|
|
|
|
TW_Reformat(pParams->twDoc, NULL);
|
|
aElements = w3doc->aElements;
|
|
for (i = pParams->nEl; i >= 0; i = aElements[i].next)
|
|
{
|
|
if (aElements[i].lFlags & ELEFLAG_BACKGROUND_IMAGE && aElements[i].myImage == target) {
|
|
if (bIsComplete)
|
|
TW_UpdateRect(pParams->twDoc, NULL); // invalidate entire window
|
|
break;
|
|
}
|
|
if (aElements[i].type == ELE_IMAGE || aElements[i].type == ELE_FORMIMAGE)
|
|
{
|
|
myImage = aElements[i].myImage;
|
|
if (myImage == target)
|
|
{
|
|
RECT rUpdate;
|
|
|
|
FrameToDoc( w3doc, i, &rUpdate );
|
|
|
|
#ifdef FEATURE_CLIENT_IMAGEMAP
|
|
if (aElements[i].lFlags & (ELEFLAG_USEMAP | ELEFLAG_IMAGEMAP | ELEFLAG_ANCHOR))
|
|
#else
|
|
if (aElements[i].lFlags & (ELEFLAG_IMAGEMAP | ELEFLAG_ANCHOR))
|
|
#endif
|
|
{
|
|
InsetRect(&rUpdate, aElements[i].border, aElements[i].border);
|
|
}
|
|
|
|
if (myImage->width)
|
|
{
|
|
OffsetRect(&rUpdate, -pParams->twDoc->offl, -pParams->twDoc->offt);
|
|
if (rUpdate.top < w3doc->ybound)
|
|
{
|
|
if (bIsComplete)
|
|
{
|
|
if (pParams->pImgUpdate)
|
|
(*(PDCIMGUPDATEPROC)(pParams->pImgUpdate))(myImage,pParams->twDoc,&rUpdate,pParams->logicalRowN+1,myImage->height-1);
|
|
else
|
|
TW_UpdateRect(pParams->twDoc, &rUpdate);
|
|
}
|
|
else
|
|
DC_UpdateRect(myImage->decoderObject,pParams->twDoc,&rUpdate,pParams->logicalRow0,pParams->logicalRowN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define STATE_FETCH_TRIEDLOAD (STATE_OTHER)
|
|
#define STATE_FETCH_SYNCLOAD (STATE_OTHER+1)
|
|
#define STATE_FETCH_MAPLOAD (STATE_OTHER+2)
|
|
|
|
static int Image_Fetch_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_Image_Fetch *pParams;
|
|
int requestID;
|
|
DECODERSTATUS dcStatus;
|
|
enum GuitErrs errorCode;
|
|
struct _www *w3doc;
|
|
struct ImageInfo *pImg = NULL;
|
|
BOOL bCausedCompletion;
|
|
BOOL bSeized = FALSE;
|
|
BOOL bWasUnknown;
|
|
pParams = *ppInfo;
|
|
#define IMG_LOADMASK (IMG_ERROR|IMG_MISSING|IMG_NOTLOADED)
|
|
|
|
pParams->request = HTRequest_validate(pParams->request);
|
|
w3doc = tw == NULL ? NULL : pParams->twDoc->w3doc;
|
|
if (w3doc == NULL) goto exitAbort;
|
|
pImg = pImgLookup(pParams->twDoc,pParams->nEl);
|
|
if (pImg == NULL) goto exitAbort;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: Called for %s [element=%d] (request=%d)\n", pImg->srcURL,pParams->nEl,cbDC_GetRequestID(pImg->decoderObject)));
|
|
pParams->childThread = NULL;
|
|
if (pImg->flags & IMG_SEIZE)
|
|
{
|
|
pImg->flags &= ~IMG_SEIZE;
|
|
bSeized = TRUE;
|
|
}
|
|
|
|
pParams->request->destination = Dest_CreateDest(pImg->srcURL);
|
|
|
|
if (!pParams->request->destination)
|
|
{
|
|
goto exitAbort;
|
|
}
|
|
|
|
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
pParams->request->referer = pParams->twDoc->w3doc->szActualURL;
|
|
pParams->logicalRow0 = -1;
|
|
pParams->logicalRowN = -1;
|
|
pParams->pImgUpdate = NULL;
|
|
{
|
|
struct Params_Image_Load *pLoadParams;
|
|
|
|
if (bSeized)
|
|
{
|
|
pParams->pStatus = GTR_CALLOC(1,sizeof(SAFERESULT));
|
|
if (pParams->pStatus == NULL)
|
|
{
|
|
return STATE_ABORT;
|
|
}
|
|
pParams->pStatus->status = TRUE;
|
|
pParams->pStatus->bSeized = TRUE;
|
|
}
|
|
else if (pParams->decoderObject &&
|
|
(pImg->flags & IMG_LOADING) &&
|
|
pParams->cbRequestID == cbDC_GetRequestID(pParams->decoderObject))
|
|
{
|
|
pLoadParams = GTR_MALLOC(sizeof(*pLoadParams));
|
|
if (pLoadParams == NULL) return STATE_ABORT;
|
|
pParams->pStatus = GTR_CALLOC(1,sizeof(SAFERESULT));
|
|
if (pParams->pStatus == NULL)
|
|
{
|
|
GTR_FREE(pLoadParams);
|
|
return STATE_ABORT;
|
|
}
|
|
|
|
pParams->request->cbRequestID = pParams->cbRequestID;
|
|
pLoadParams->request = pParams->request;
|
|
pLoadParams->pStatus = pParams->pStatus;
|
|
pLoadParams->decoderObject = pParams->decoderObject;
|
|
pLoadParams->cbRequestID = pParams->cbRequestID;
|
|
pLoadParams->nEl = pParams->nEl;
|
|
pLoadParams->twDoc = pParams->twDoc;
|
|
pParams->pStatus->refcnt++;
|
|
tw->image_request = NULL;
|
|
pLoadParams->parentThread = Async_GetCurrentThread();
|
|
|
|
/* It is important that HTLoadDocument_Async and it's descendents
|
|
pass around twDoc, which is a "REAL" Mwin. this is for error
|
|
reporting and wait cursor manipulation */
|
|
|
|
pParams->childThread = Async_StartThread(Image_Load_Async, pLoadParams, pParams->twDoc);
|
|
}
|
|
}
|
|
return STATE_FETCH_TRIEDLOAD;
|
|
|
|
case STATE_FETCH_TRIEDLOAD:
|
|
// NOTE: the first thread to call cbDC_BlockOnCompletion will
|
|
// set pImg->decoderObject to NULL.
|
|
requestID = -1;
|
|
errorCode = errNoError;
|
|
dcStatus = DC_Complete;
|
|
if (pImg->decoderObject)
|
|
{
|
|
requestID = cbDC_GetRequestID(pImg->decoderObject);
|
|
dcStatus = cbDC_BlockOnCompletion(pImg->decoderObject,&errorCode);
|
|
bCausedCompletion = requestID != cbDC_GetRequestID(pImg->decoderObject);
|
|
}
|
|
switch (dcStatus)
|
|
{
|
|
case DC_Complete:
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: Complete for %s (request=%d)\n", pImg->srcURL,requestID));
|
|
break;
|
|
case DC_WHKnown:
|
|
{
|
|
PIMGCBINFO pImgCBInfo = pDC_GetOutput(pImg->decoderObject);
|
|
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: w&h known for %s (request=%d) [w=%d h=%d]\n", pImg->srcURL,requestID,pImgCBInfo->width,pImgCBInfo->height));
|
|
|
|
bWasUnknown = !(pImg->flags & IMG_WHKNOWN);
|
|
pImg->width = pImgCBInfo->width;
|
|
pImg->height = pImgCBInfo->height;
|
|
pImg->flags |= IMG_WHKNOWN;
|
|
if (bWasUnknown) (void) W3Doc_CheckForImageLoad(w3doc);
|
|
TW_Reformat(pParams->twDoc, NULL);
|
|
}
|
|
Async_UnblockConditionally(MasterFilter,pParams->tw->twParent);
|
|
return STATE_FETCH_TRIEDLOAD;
|
|
case DC_ProgDraw:
|
|
{
|
|
PIMGCBINFO pImgCBInfo = pDC_GetOutput(pImg->decoderObject);
|
|
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: prog draw [%d] for %s (request=%d)\n", pImgCBInfo->logicalRow, pImg->srcURL,requestID));
|
|
pImg->width = pImgCBInfo->width;
|
|
pImg->height = pImgCBInfo->height;
|
|
pImg->flags |= IMG_WHKNOWN;
|
|
pParams->pImgUpdate = pDC_GetImgUpdate(pImg->decoderObject);
|
|
pParams->logicalRow0 = pParams->logicalRowN+1;
|
|
pParams->logicalRowN = pImgCBInfo->logicalRow;
|
|
DoUpdateImg(pParams,FALSE);
|
|
pImgCBInfo->bProgSeen = TRUE;
|
|
Async_UnblockConditionally(MasterFilter,pParams->tw->twParent);
|
|
}
|
|
return STATE_FETCH_TRIEDLOAD;
|
|
default:
|
|
return STATE_FETCH_TRIEDLOAD;
|
|
}
|
|
if (bCausedCompletion)
|
|
{
|
|
pImg->decoderObject = NULL;
|
|
if (errorCode == errNoError &&
|
|
((!pParams->pStatus) ||
|
|
pParams->pStatus->refcnt != 0 ||
|
|
pParams->pStatus->status) &&
|
|
!(pParams->request->iFlags & HTREQ_HAD_ERROR) &&
|
|
pImg->width &&
|
|
pImg->height)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Fetching %s succeeded\n", pImg->srcURL));
|
|
if (wg.eColorMode == 8)
|
|
{
|
|
#ifdef FEATURE_JPEG
|
|
if (pImg->flags & IMG_JPEG)
|
|
{
|
|
pImg->pbmi = BIT_Make_DIB_PAL_Header_Prematched(pImg->width, pImg->height,
|
|
pImg->data);
|
|
pImg->flags |= IMG_PREMATCHED;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* if the data is already dithered due to progressive draw
|
|
pImg->flags & IMG_PREMATCHED is true and we pass
|
|
NULL for data to prevent redithering
|
|
*/
|
|
pImg->pbmi = BIT_Make_DIB_PAL_Header(pImg->width, pImg->height,
|
|
pImg->flags & IMG_PREMATCHED ? NULL:pImg->data,
|
|
pImg->hPalette, pImg->transparent);
|
|
pImg->flags |= IMG_PREMATCHED;
|
|
/*
|
|
We no longer need the image's palette
|
|
*/
|
|
if (pImg->hPalette)
|
|
{
|
|
DeleteObject(pImg->hPalette);
|
|
pImg->hPalette = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wg.eColorMode == 4)
|
|
{
|
|
#ifdef FEATURE_JPEG
|
|
if (pImg->flags & IMG_JPEG)
|
|
{
|
|
pImg->pbmi = BIT_Make_DIB_RGB_Header_VGA(pImg->width, pImg->height,
|
|
pImg->data);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pImg->pbmi = BIT_Make_DIB_RGB_Header_Screen(pImg->width, pImg->height,
|
|
pImg->data, pImg->hPalette, pImg->transparent, pImg->flags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef FEATURE_JPEG
|
|
/* true color display */
|
|
if (pImg->flags & IMG_JPEG)
|
|
{
|
|
pImg->pbmi = BIT_Make_DIB_RGB_Header_24BIT(pImg->width, pImg->height,
|
|
pImg->data);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pImg->pbmi = BIT_Make_DIB_RGB_Header_Screen(pImg->width, pImg->height,
|
|
pImg->data, pImg->hPalette, pImg->transparent, pImg->flags);
|
|
}
|
|
}
|
|
}
|
|
/* If Image is equal to old version, swap back */
|
|
if (pImg->pImgOtherVers &&
|
|
pImg->cbCheckSum == pImg->pImgOtherVers->cbCheckSum)
|
|
{
|
|
Image_Revert(pImg);
|
|
Image_Swap(w3doc,pImg,pImg->pImgOtherVers);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* If we don't already have error flags, make something up. If the global
|
|
abort flag is set, assume that that's why the load failed. Otherwise,
|
|
it must have been a bona fide error. */
|
|
if (!pImg->flags || (pImg->flags & IMG_NOTLOADED) ||
|
|
(pParams->request->iFlags & HTREQ_HAD_ERROR) )
|
|
pImg->flags = IMG_ERROR | (pImg->flags & ~IMG_LOADMASK);
|
|
if (pParams->pImgThreads->errElement < 0 ||
|
|
pParams->pImgThreads->errElement > pParams->nEl)
|
|
pParams->pImgThreads->errElement = pParams->nEl;
|
|
/* If backup Image, swap back */
|
|
if (pImg->pImgOtherVers)
|
|
{
|
|
Image_Revert(pImg);
|
|
Image_Swap(w3doc,pImg,pImg->pImgOtherVers);
|
|
pParams->logicalRowN = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(pImg->flags & IMG_ERROR))
|
|
{
|
|
/* If Image is equal to old version, swap back */
|
|
if (pImg->pImgOtherVers &&
|
|
pImg->cbCheckSum == pImg->pImgOtherVers->cbCheckSum)
|
|
{
|
|
Image_Revert(pImg);
|
|
Image_Swap(w3doc,pImg,pImg->pImgOtherVers);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pParams->pImgThreads->errElement < 0 ||
|
|
pParams->pImgThreads->errElement > pParams->nEl)
|
|
pParams->pImgThreads->errElement = pParams->nEl;
|
|
/* If backup Image, swap back */
|
|
if (pImg->pImgOtherVers)
|
|
{
|
|
Image_Revert(pImg);
|
|
Image_Swap(w3doc,pImg,pImg->pImgOtherVers);
|
|
pParams->logicalRowN = -1;
|
|
}
|
|
}
|
|
}
|
|
// Image_Swap may have hosed pImg!
|
|
pImg = pImgLookup(pParams->twDoc,pParams->nEl);
|
|
DoUpdateImg(pParams,TRUE);
|
|
if (pParams->pStatus && pParams->pStatus->refcnt != 0)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: block on load of %s\n",pImg ? pImg->srcURL:"Dead Image"));
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
}
|
|
return STATE_FETCH_SYNCLOAD;
|
|
|
|
case STATE_FETCH_SYNCLOAD:
|
|
if (pParams->decoderObject)
|
|
{
|
|
if (pParams->pStatus->status)
|
|
{
|
|
#ifdef FEATURE_CLIENT_IMAGEMAP
|
|
struct _element *aElements = w3doc->aElements;
|
|
#endif
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: download %s succeeded\n", pImg->srcURL));
|
|
#ifdef FEATURE_CLIENT_IMAGEMAP
|
|
if (aElements[pParams->nEl].lFlags & ELEFLAG_USEMAP &&
|
|
aElements[pParams->nEl].myMap->flags == MAP_NOTLOADED)
|
|
{
|
|
struct Params_Map_Fetch *pmf;
|
|
|
|
pmf = GTR_MALLOC(sizeof(*pmf));
|
|
if (pmf == NULL) return STATE_ABORT;
|
|
pmf->pMap = aElements[pParams->nEl].myMap;
|
|
pmf->fNotFromCache = pParams->request->fNotFromCache;
|
|
Async_DoCall(Map_Fetch_Async, pmf);
|
|
return STATE_FETCH_MAPLOAD;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("Image_Fetch_Async: download %s failed\n", pImg->srcURL));
|
|
}
|
|
}
|
|
/* FALL THROUGH */
|
|
|
|
case STATE_FETCH_MAPLOAD:
|
|
goto exitDone;
|
|
|
|
case STATE_ABORT:
|
|
// if someone aborts we can't be sure whether its really an error.
|
|
if ( pImg )
|
|
pImg->flags = (~IMG_ERROR & pImg->flags);
|
|
goto exitAbort;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
|
|
exitAbort:
|
|
if (bExistsAnother(pParams->pStatus) && pParams->childThread)
|
|
{
|
|
Async_TerminateThread(pParams->childThread);
|
|
}
|
|
|
|
if (pParams->decoderObject && pParams->cbRequestID == cbDC_GetRequestID(pParams->decoderObject))
|
|
{
|
|
DC_Abort(pParams->decoderObject);
|
|
if (pImg)
|
|
{
|
|
pImg->decoderObject = NULL;
|
|
pImg->flags &= ~IMG_LOADING;
|
|
if (pImg->pImgOtherVers)
|
|
{
|
|
Image_Revert(pImg);
|
|
if (w3doc) Image_Swap(w3doc,pImg,pImg->pImgOtherVers);
|
|
}
|
|
}
|
|
}
|
|
|
|
exitDone:
|
|
if (pParams->bWasVisible) pParams->pImgThreads->status--;
|
|
pParams->pImgThreads = pSafeFree(pParams->pImgThreads);
|
|
#ifdef XX_DEBUG
|
|
if (pParams->pImgThreads == NULL) XX_DMsg(DBG_IMAGE, ("dangling pointer in ImageFetch\n"));
|
|
#endif
|
|
if (pParams->pImgThreads != NULL)
|
|
{
|
|
Async_UnblockConditionally(MasterFilter,pParams->tw->twParent);
|
|
}
|
|
bSeized = (pParams->pStatus != NULL) && pParams->pStatus->bSeized;
|
|
pParams->pStatus = pSafeFree(pParams->pStatus);
|
|
if (tw && pParams->request && pParams->pStatus == NULL && !bSeized)
|
|
{
|
|
Nuke_Request(pParams->request);
|
|
if (tw->image_request == pParams->request) tw->image_request = NULL;
|
|
}
|
|
if (tw) Plan_close(pParams->tw);
|
|
|
|
return STATE_DONE;
|
|
}
|
|
|
|
void Image_DeleteAll(void)
|
|
{
|
|
int i;
|
|
int count;
|
|
struct ImageInfo *img;
|
|
|
|
count = Hash_Count(&gImageCache);
|
|
XX_DMsg(DBG_IMAGE, ("Image_DeleteAll: count=%d\n", count));
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
Hash_GetIndexedEntry(&gImageCache, i, NULL, NULL, (void **) &img);
|
|
#ifdef XX_DEBUG
|
|
if (img->refCount)
|
|
XX_DMsg(DBG_IMAGE, ("RefCount = %d in Image_DeleteAll, URL = %s\n", img->refCount,img->srcURL));
|
|
#endif
|
|
x_DisposeImage(img, TRUE);
|
|
}
|
|
Hash_FreeContents(&gImageCache);
|
|
}
|
|
|
|
|
|
// FilterProc for the unblock conditionally on loadall blocked for parser
|
|
static boolean ParseFilter(ThreadID theThread,void *context)
|
|
{
|
|
struct Mwin *tw = Async_GetWindowFromThread(theThread);
|
|
|
|
if (tw && TW_GETBLOCKED(tw,TW_PARSEBLOCKED) &&
|
|
(tw->twParent == context || (tw->twParent == NULL && tw == context)))
|
|
{
|
|
TW_CLEARBLOCKED(tw,TW_PARSEBLOCKED);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
void Image_UnblockMaster(struct Mwin *twDoc)
|
|
{
|
|
Async_UnblockConditionally(ParseFilter,twDoc);
|
|
}
|
|
|
|
static BOOL bImgDuplicate(struct _element *aElements,int nElLast,struct ImageInfo *myImage)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i != nElLast;i = aElements[i].next)
|
|
{
|
|
if (aElements[i].type == ELE_IMAGE || aElements[i].type == ELE_FORMIMAGE)
|
|
if (aElements[i].myImage == myImage) return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
#ifdef DEBUG
|
|
extern BOOL bFavorVisibleImages;
|
|
#endif
|
|
#endif
|
|
|
|
/* Load all images in the document which are marked as IMG_NOTLOADED */
|
|
#define STATE_LOADALL_GETNEXT (STATE_OTHER)
|
|
#define STATE_LOADALL_GOTONE (STATE_OTHER+1)
|
|
#define STATE_LOADALL_WAITING (STATE_OTHER+2)
|
|
#define STATE_LOADALL_REAPING (STATE_OTHER+3)
|
|
int Image_LoadAll_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
struct Params_Image_LoadAll *pParams = *ppInfo;
|
|
struct _element *aElements;
|
|
struct _www *w3doc = NULL;
|
|
struct Mwin *twParent = NULL;
|
|
int nLastGood;
|
|
BOOL bWasVisible;
|
|
BOOL bFileURL;
|
|
struct ImageInfo *myImage;
|
|
|
|
if (tw)
|
|
{
|
|
twParent = pParams->tw->twParent ? pParams->tw->twParent : pParams->tw;
|
|
w3doc = twParent->w3doc;
|
|
}
|
|
if (w3doc == NULL)
|
|
{
|
|
if (nState != STATE_INIT) nState = STATE_ABORT;
|
|
}
|
|
else aElements = w3doc->aElements;
|
|
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
if (!pParams->bJustOne) pParams->nEl = 0;
|
|
if (pParams->bJustOne || (pParams->tw->twParent == NULL))
|
|
{
|
|
WAIT_Push(tw, waitPartialInteract, "");
|
|
}
|
|
tw->dwSslPageFlagsWorking &= ~SSL_PAGE_MIXED_WARNING_GIVEN;
|
|
pParams->nLastDone = -1;
|
|
pParams->decoderObject = NULL;
|
|
pParams->cbImgLoadCount = gcbImgLoadCount;
|
|
pParams->pImgThreads = GTR_CALLOC(1,sizeof(SAFEIMGRESULT));
|
|
pParams->pImgThreads->errElement = -1;
|
|
pParams->bInRecovery = FALSE;
|
|
pParams->bDontGoToNextEle = FALSE;
|
|
|
|
if (pParams->pImgThreads == NULL) goto exitDone;
|
|
if (w3doc == NULL) goto exitDone;
|
|
|
|
// NOTE: we ALWAYS load image for HTML cons'ed to show link to image
|
|
if (w3doc->bIsImage) pParams->bLocalOnly = FALSE;
|
|
|
|
WAIT_SetStatusBarIcon( tw, SBI_ReceivingFromIcon );
|
|
return STATE_LOADALL_GETNEXT;
|
|
|
|
case STATE_LOADALL_GETNEXT:
|
|
/* Find the next unloaded picture */
|
|
nLastGood = pParams->nEl;
|
|
if (w3doc->elementCount == 0) pParams->nEl = -1;
|
|
else if (nLastGood == pParams->nLastDone)
|
|
pParams->nEl = aElements[nLastGood].next;
|
|
for ( ; pParams->nEl >= 0; pParams->nEl = aElements[pParams->nEl].next)
|
|
{
|
|
|
|
// Is this a blob?????
|
|
// Don't attempt to load blob here if hidden element (used for background
|
|
// download of VRML inline files)
|
|
//
|
|
if (aElements[pParams->nEl].pblob &&
|
|
((!(aElements[pParams->nEl].lFlags & ELEFLAG_HIDDEN)) ||
|
|
pParams->bJustOne))
|
|
{
|
|
|
|
struct Params_LoadBackgroundBlobs *pPLBB;
|
|
PDECODER pDecoder;
|
|
|
|
// if its the first time we hit this code for a particalar element
|
|
// and this is NOT a "Load Just One item", and there is a LowSrc
|
|
// image, then we must hack it so the LowSrc image
|
|
// is downloaded first
|
|
if ( pParams->bDontGoToNextEle == FALSE && !pParams->bJustOne &&
|
|
aElements[pParams->nEl].myImage && aElements[pParams->nEl].myImage->srcURL)
|
|
{
|
|
// keep us from going to next element until we have the
|
|
// image and the Blob downloaded.
|
|
pParams->bDontGoToNextEle = TRUE;
|
|
goto LDo_Image_First;
|
|
}
|
|
LDo_CancelImageFirst: // for some reason we decided not to download this image first
|
|
|
|
// make sure we Go to the next element
|
|
pParams->bDontGoToNextEle = FALSE;
|
|
|
|
pDecoder = pDC_Reserve(pParams->tw,pParams->bInRecovery);
|
|
if (NULL == pDecoder)
|
|
{
|
|
// hack to prevent infinite loop in recovery mode
|
|
// if there is good AVI but bad image on the same element
|
|
if ( pParams->bInRecovery &&
|
|
( (aElements[pParams->nEl].type == ELE_IMAGE && !gPrefs.bAutoLoadVideos )
|
|
|| (aElements[pParams->nEl].pblob->dwFlags & (BLOB_FLAGS_LOADED|BLOB_FLAGS_LOADING|BLOB_FLAGS_ERROR) ) ) &&
|
|
aElements[pParams->nEl].myImage && aElements[pParams->nEl].myImage->srcURL )
|
|
goto LDo_Image_First;
|
|
else if ( !pParams->bInRecovery &&
|
|
(aElements[pParams->nEl].type == ELE_IMAGE && !gPrefs.bAutoLoadVideos ))
|
|
return STATE_LOADALL_GOTONE;
|
|
|
|
return STATE_LOADALL_GETNEXT;
|
|
}
|
|
|
|
pPLBB = GTR_MALLOC(sizeof(*pPLBB));
|
|
if (NULL == pPLBB){
|
|
makeAvailable(pDecoder);
|
|
return STATE_ABORT;
|
|
}
|
|
pPLBB->twDoc = twParent;
|
|
pPLBB->iIndex = pParams->nEl;
|
|
pPLBB->pImgThreads = pParams->pImgThreads;
|
|
pPLBB->thidParent = Async_GetCurrentThread();
|
|
pPLBB->bLocalOnly = pParams->bLocalOnly;
|
|
pPLBB->bNoImageCache = pParams->bNoImageCache;
|
|
pPLBB->pDecoder = pDecoder;
|
|
|
|
// need to propage this flag to the blob code,
|
|
// since we figure out whether to generate errors or not
|
|
pPLBB->bJustOne = pParams->bJustOne;
|
|
|
|
pParams->pImgThreads->refcnt++;
|
|
|
|
Async_StartThread(LoadBackgroundBlobs_Async, pPLBB,twParent);
|
|
// move on to the next element.
|
|
return STATE_LOADALL_GOTONE;
|
|
}
|
|
|
|
|
|
LDo_Image_First:
|
|
nLastGood = pParams->nEl;
|
|
if (aElements[pParams->nEl].type == ELE_IMAGE || aElements[pParams->nEl].type == ELE_FORMIMAGE)
|
|
{
|
|
if (aElements[pParams->nEl].myImage->srcURL)
|
|
{
|
|
myImage = aElements[pParams->nEl].myImage;
|
|
bFileURL = !strncmp(myImage->srcURL, "file:", 5);
|
|
if (((pParams->bInRecovery && !bFileURL) || pParams->bJustOne) &&
|
|
(myImage->flags & IMG_ERROR))
|
|
myImage->flags = IMG_NOTLOADED;
|
|
if ((myImage->flags & IMG_NOTLOADED) ||
|
|
(pParams->bNoImageCache && myImage->cbImgLoadCount < pParams->cbImgLoadCount))
|
|
{
|
|
/* This is an unloaded picture. Do we want to load it? */
|
|
if ((!pParams->bLocalOnly) ||
|
|
bFileURL ||
|
|
(myImage->flags & IMG_LOADING))
|
|
{
|
|
/* NOTE: we only spawn one lightweight thread per image/doc pair */
|
|
|
|
if (!(myImage->flags & IMG_LOADING) || !bImgDuplicate(aElements,pParams->nEl,myImage))
|
|
{
|
|
/* Yes, proceed */
|
|
|
|
myImage->flags &= ~IMG_LOADSUP;
|
|
break;
|
|
}
|
|
}
|
|
if (pParams->bLocalOnly)
|
|
{
|
|
myImage->flags |= IMG_LOADSUP;
|
|
TW_Reformat(twParent, NULL);
|
|
aElements = w3doc->aElements;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we failed to proceed with downloading the
|
|
// image, then don't skip this current BLOB !
|
|
if ( pParams->bDontGoToNextEle )
|
|
goto LDo_CancelImageFirst;
|
|
|
|
} // end of for loop
|
|
|
|
if (pParams->nEl < 0)
|
|
{
|
|
XX_Assert((!pParams->bDontGoToNextEle),
|
|
("Come Get ArthurBI RIGHT NOW. I'm asserting that this case won't happen, when nEl < 0 and bDontGoToNextEle is TRUE"));
|
|
if ((!pParams->bJustOne) && twParent->bLoading)
|
|
{
|
|
pParams->nEl = nLastGood;
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
TW_SETBLOCKED(pParams->tw,TW_PARSEBLOCKED);
|
|
return STATE_LOADALL_GETNEXT;
|
|
}
|
|
XX_DMsg(DBG_IMAGE, ("last element inspected %d of %d\n", nLastGood,w3doc->elementCount));
|
|
if (pParams->pImgThreads->refcnt)
|
|
{
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
TW_SETBLOCKED(pParams->tw,TW_REAPBLOCKED);
|
|
return STATE_LOADALL_REAPING;
|
|
}
|
|
|
|
/* There are no more unloaded pictures */
|
|
w3doc->bHasMissingImages = W3Doc_HasMissingImages(w3doc);
|
|
goto exitDone;
|
|
}
|
|
|
|
/* OK, let's bring in this picture */
|
|
{
|
|
struct Params_Image_Fetch *pif;
|
|
|
|
if (aElements[pParams->nEl].myImage->flags & IMG_SEIZE)
|
|
{
|
|
bWasVisible = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bWasVisible = bImgCheckForVisible(w3doc,pParams->nEl);
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
#ifdef DEBUG
|
|
if (bFavorVisibleImages)
|
|
{
|
|
#endif
|
|
#endif
|
|
if ((!bWasVisible) && pParams->pImgThreads->status)
|
|
{
|
|
XX_DMsg(DBG_IMAGE, ("blocking invisible element %d,visible threads = %d\n", pParams->nEl,pParams->pImgThreads->status));
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
TW_SETBLOCKED(pParams->tw,TW_VISBLOCKED);
|
|
// we're blocking on an invisible element
|
|
// therefore we should NOT get our BLOB
|
|
// since we need to re-enter to this state
|
|
// and continue getting this image.
|
|
pParams->bDontGoToNextEle = FALSE;
|
|
return STATE_LOADALL_GETNEXT;
|
|
}
|
|
#ifdef TEST_DCACHE_OPTIONS
|
|
#ifdef DEBUG
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
// reserve a decoder thread - if all are in use, NULL will be
|
|
// returned and we will be blocked as side effect
|
|
if (aElements[pParams->nEl].myImage->decoderObject == NULL)
|
|
{
|
|
pParams->decoderObject = pDC_Reserve(pParams->tw,pParams->bInRecovery);
|
|
if (pParams->decoderObject == NULL)
|
|
{
|
|
// make sure to not get a blob on reentry to this
|
|
// state. Since we're not finished on this image
|
|
// We're waiting for a decoder thread
|
|
pParams->bDontGoToNextEle = FALSE;
|
|
return STATE_LOADALL_GETNEXT;
|
|
}
|
|
}
|
|
|
|
pif = GTR_CALLOC(1,sizeof(*pif));
|
|
if (pif == NULL) return STATE_ABORT;
|
|
if (!(aElements[pParams->nEl].myImage->flags & IMG_NOTLOADED))
|
|
Image_Nuke(w3doc,pParams->nEl,TRUE, TRUE);
|
|
|
|
myImage = aElements[pParams->nEl].myImage;
|
|
pif->tw = NewMwin(GIMGSLAVE);
|
|
if (pif->tw == NULL) return STATE_ABORT;
|
|
if (!(myImage->flags & IMG_LOADING))
|
|
{
|
|
myImage->flags |= IMG_LOADING;
|
|
myImage->cbImgLoadCount = gcbImgLoadCount++;
|
|
}
|
|
if (pParams->decoderObject)
|
|
myImage->decoderObject = pParams->decoderObject;
|
|
if (myImage->flags & IMG_SEIZE)
|
|
{
|
|
myImage->cbImgLoadCount = gcbImgLoadCount++;
|
|
pParams->decoderObject = myImage->decoderObject;
|
|
}
|
|
pif->tw->twParent = pParams->tw;
|
|
pif->decoderObject = pParams->decoderObject;
|
|
pif->pImgThreads = pParams->pImgThreads;
|
|
pif->bWasVisible = bWasVisible;
|
|
pParams->decoderObject = NULL;
|
|
if (bWasVisible) pParams->pImgThreads->status++;
|
|
pParams->pImgThreads->refcnt++;
|
|
pif->request = pif->tw->image_request;
|
|
// if we're not loading them individually then
|
|
// don't force an error message on each one.
|
|
if ( !pParams->bJustOne )
|
|
pif->request->iFlags |= HTREQ_STOP_WHINING;
|
|
pif->request->fNotFromCache = pParams->bNoImageCache;
|
|
pif->twDoc = twParent;
|
|
pif->nEl = pParams->nEl;
|
|
if (pif->decoderObject) pif->cbRequestID = cbDC_GetRequestID(pif->decoderObject);
|
|
pif->request->destination = NULL;
|
|
Async_StartThread(Image_Fetch_Async, pif,pif->tw);
|
|
}
|
|
return STATE_LOADALL_GOTONE;
|
|
|
|
|
|
case STATE_LOADALL_GOTONE:
|
|
// if we had an error and we aren't already in recovery
|
|
// we wait for all threads to finish to minimize load
|
|
// on server
|
|
if (pParams->pImgThreads->errElement >= 0 &&
|
|
(!pParams->bInRecovery) &&
|
|
(!pParams->bLocalOnly) &&
|
|
(!pParams->bJustOne))
|
|
return STATE_LOADALL_REAPING;
|
|
|
|
|
|
// if we've downloaded an image for a blob, then we need
|
|
// to go back and download the Blob itself, NOW
|
|
if ( pParams->bDontGoToNextEle == TRUE )
|
|
return STATE_LOADALL_GETNEXT;
|
|
|
|
|
|
nLastGood = pParams->nEl;
|
|
pParams->nLastDone = pParams->nEl;
|
|
if (pParams->bJustOne)
|
|
{
|
|
pParams->nEl = -1;
|
|
}
|
|
else
|
|
{
|
|
pParams->nEl = aElements[pParams->nEl].next;
|
|
if (pParams->nEl < 0) pParams->nEl = nLastGood;
|
|
}
|
|
return STATE_LOADALL_GETNEXT;
|
|
|
|
case STATE_LOADALL_REAPING:
|
|
/* There are no more unloaded pictures */
|
|
if (pParams->pImgThreads->refcnt)
|
|
{
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
TW_SETBLOCKED(pParams->tw,TW_REAPBLOCKED);
|
|
return STATE_LOADALL_REAPING;
|
|
}
|
|
// if we had an error and we aren't already in recovery
|
|
// we wait for all threads to finish to minimize load
|
|
// on server
|
|
if (pParams->pImgThreads->errElement >= 0 &&
|
|
(!pParams->bInRecovery) &&
|
|
(!pParams->bLocalOnly) &&
|
|
(!pParams->bJustOne))
|
|
{
|
|
if (twParent->bLoading)
|
|
{
|
|
Async_BlockThread(Async_GetCurrentThread());
|
|
TW_SETBLOCKED(pParams->tw,TW_PARSEBLOCKED);
|
|
return STATE_LOADALL_REAPING;
|
|
}
|
|
|
|
XX_DMsg(DBG_IMAGE, ("entering recovery mode starting at element %d\n", pParams->pImgThreads->errElement));
|
|
pParams->nLastDone = -1;
|
|
pParams->nEl = pParams->pImgThreads->errElement;
|
|
pParams->bInRecovery = TRUE;
|
|
// if we're going back make sure we don't loop infinitely
|
|
// trying to get this AVI, since we're reseting the element counter
|
|
pParams->bDontGoToNextEle = FALSE;
|
|
return STATE_LOADALL_GETNEXT;
|
|
}
|
|
|
|
w3doc->bHasMissingImages = W3Doc_HasMissingImages(w3doc);
|
|
goto exitDone;
|
|
|
|
case STATE_ABORT:
|
|
if(pParams->decoderObject) DC_Abort(pParams->decoderObject);
|
|
if (w3doc)
|
|
{
|
|
Async_TerminateByWindow(tw);
|
|
(void) W3Doc_CheckForImageLoad(w3doc);
|
|
w3doc->bIsShowPlaceholders = TRUE; // show unloaded images as placeholders
|
|
TW_Reformat(twParent, NULL);
|
|
w3doc->bHasMissingImages = W3Doc_HasMissingImages(w3doc);
|
|
}
|
|
|
|
goto exitDone;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
exitDone:
|
|
if (tw)
|
|
{
|
|
if (pParams->bJustOne || (pParams->tw->twParent == NULL))
|
|
{
|
|
WAIT_Pop(tw);
|
|
}
|
|
|
|
if (twParent = pParams->tw->twParent)
|
|
{
|
|
TW_CLEARFLAG(twParent,TW_LOADALLACTIVE);
|
|
if (TW_GETBLOCKED(twParent,TW_LOADALLBLOCKED))
|
|
{
|
|
TW_CLEARBLOCKED(twParent,TW_LOADALLBLOCKED);
|
|
Async_UnblockThread(pParams->parentThread);
|
|
}
|
|
Plan_close(pParams->tw);
|
|
}
|
|
else
|
|
{
|
|
TW_CLEARBLOCKED(pParams->tw,TW_REAPBLOCKED);
|
|
TW_CLEARBLOCKED(pParams->tw,TW_VISBLOCKED);
|
|
TW_CLEARBLOCKED(pParams->tw,TW_PARSEBLOCKED);
|
|
}
|
|
}
|
|
if (pParams->pImgThreads) pParams->pImgThreads = pSafeFree(pParams->pImgThreads);
|
|
#ifdef XX_DEBUG
|
|
if (pParams->pImgThreads) XX_DMsg(DBG_IMAGE, ("dangling pointer in LoadAllImages\n"));
|
|
#endif
|
|
return STATE_DONE;
|
|
}
|
|
|
|
// Counts the number of occurences of pImg in w3doc.
|
|
int Image_CountRefs(struct _www *w3doc,struct ImageInfo *pImg)
|
|
{
|
|
int cbCount = 0;
|
|
struct _element *aElements = w3doc->aElements;
|
|
int i;
|
|
|
|
if (pImg->refCount == 1) return 1;
|
|
|
|
for (i = 0; i >= 0; i = aElements[i].next)
|
|
{
|
|
if ((aElements[i].type == ELE_IMAGE || aElements[i].type == ELE_FORMIMAGE) && aElements[i].myImage == pImg)
|
|
cbCount++;
|
|
}
|
|
return cbCount;
|
|
}
|
|
|
|
// Swaps one version of an image for another.
|
|
static void Image_Swap(struct _www *w3doc,struct ImageInfo *pOld,struct ImageInfo *pNew)
|
|
{
|
|
struct _element *aElements = w3doc->aElements;
|
|
int i;
|
|
|
|
for (i = 0; i >= 0; i = aElements[i].next)
|
|
{
|
|
if ((aElements[i].type == ELE_IMAGE || aElements[i].type == ELE_FORMIMAGE) && aElements[i].myImage == pOld)
|
|
{
|
|
Image_AddRef(pNew);
|
|
Image_NukeRef(pOld);
|
|
aElements[i].myImage = pNew;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if pOld is the image in the hash table (current version bound to URL), takes
|
|
// pOld out of hash table (and performs equivalent with persistent dcache) and puts
|
|
// pOld->pImgOtherVers instead.
|
|
// ASSUMES that pOld->pImgOtherVers != NULL
|
|
static void Image_Revert(struct ImageInfo *pOld)
|
|
{
|
|
int ndx;
|
|
struct ImageInfo *pHashImg;
|
|
struct ImageInfo *pNew = pOld->pImgOtherVers;
|
|
|
|
XX_Assert((pNew != NULL), ("Image Revert called w/o old vers: %s", pOld->srcURL));
|
|
ndx = Hash_Find(&gImageCache, pOld->srcURL, NULL, (void **) &pHashImg);
|
|
if (pHashImg == pOld)
|
|
{
|
|
// DEEPAK: here you put pOld in aux cache and remove it from real cache
|
|
MoveDCacheEntryToAux(pOld->actualURL, pOld);
|
|
Hash_DeleteIndexedEntry(&gImageCache, ndx);
|
|
// DEEPAK: here you remove pNew from aux cache and put it back into real cache
|
|
MoveAuxEntryToDCache(pNew);
|
|
Hash_Add(&gImageCache, pNew->srcURL, NULL, (void *) pNew);
|
|
}
|
|
}
|
|
|
|
// If exists, returns persistent cache path corresponding to the image. The
|
|
// caller is responsible for freeing the string.
|
|
char * pImage_GetDCachePath(PCImageInfo pImg)
|
|
{
|
|
char *pPath = NULL;
|
|
char *pResolvedURL = NULL;
|
|
int ndx;
|
|
struct ImageInfo *pHashImg;
|
|
|
|
if (!gPrefs.bEnableDiskCache)
|
|
return NULL;
|
|
|
|
ndx = Hash_Find(&gImageCache, pImg->srcURL, NULL, (void **) &pHashImg);
|
|
if (pHashImg == pImg)
|
|
{
|
|
// Image is the current value for URL
|
|
pResolvedURL = GetResolvedURL(pImg->actualURL,NULL,NULL,&pPath,NULL,TRUE);
|
|
}
|
|
else
|
|
{
|
|
// Image (pImg) is in the auxilliary cache (we hope)
|
|
// DEEPAK: here you'd do the aux cache version of GetResolvedURL
|
|
pResolvedURL = GetResolvedURLAux(pImg, &pPath);
|
|
}
|
|
|
|
if (pResolvedURL) GTR_FREE(pResolvedURL);
|
|
return pPath;
|
|
}
|
|
|
|
|
|
// Frees image info for image at element cbElement in w3doc. In the case of
|
|
// FEATURE_IMG_THREADS, creates a new version of the image instead, if there
|
|
// exist references to the image from some other w3doc.
|
|
static BOOL Image_Nuke(struct _www *w3doc,int cbElement,BOOL bNukeMaps, BOOL bNukeDCache)
|
|
{
|
|
BOOL bSomethingNuked = FALSE;
|
|
struct _element *anElement = &w3doc->aElements[cbElement];
|
|
struct ImageInfo *myImage = anElement->myImage;
|
|
int cbRefs;
|
|
int ndx;
|
|
struct ImageInfo *pNewImg;
|
|
struct ImageInfo *pHashImg;
|
|
|
|
if (bNukeDCache && gPrefs.bEnableDiskCache && anElement->pblob &&
|
|
anElement->pblob->szURL )
|
|
{
|
|
// Note: We're counting on the blob to be removed from
|
|
anElement->pblob->dwFlags &= ~( BLOB_FLAGS_LOADING | BLOB_FLAGS_FIXUP | BLOB_FLAGS_LOADED );
|
|
FlushDCacheEntry(anElement->pblob->szURL);
|
|
}
|
|
|
|
|
|
if ((!(myImage->flags & (IMG_NOTLOADED | IMG_ERROR | IMG_MISSING))) &&
|
|
(cbRefs = Image_CountRefs(w3doc,myImage)) != myImage->refCount)
|
|
{
|
|
ndx = Hash_Find(&gImageCache, myImage->srcURL, NULL, (void **) &pHashImg);
|
|
if (pHashImg == myImage)
|
|
{
|
|
// DEEPAK: here you would delete myImage from real cache and move
|
|
// it into aux cache
|
|
MoveDCacheEntryToAux(myImage->actualURL, myImage);
|
|
Hash_DeleteIndexedEntry(&gImageCache, ndx);
|
|
}
|
|
// DEEPAK: i don't know if you do anything, but pNewImg will become
|
|
// the real cache entry for this URL once HTLoad is started.
|
|
/* I don't do anything on this because this is just a place holder. The
|
|
* dcache entry for this gets created in the HTLoad routines later on.
|
|
*/
|
|
pNewImg = Image_CreatePlaceholder(myImage->srcURL,0,0);
|
|
if (pNewImg == NULL)
|
|
{
|
|
Hash_Add(&gImageCache, myImage->srcURL, NULL, (void *) myImage);
|
|
return bSomethingNuked;
|
|
}
|
|
else
|
|
{
|
|
pNewImg->pImgOtherVers = myImage;
|
|
myImage->pImgOtherVers = pNewImg;
|
|
Image_Swap(w3doc,myImage,pNewImg);
|
|
}
|
|
bSomethingNuked = TRUE;
|
|
|
|
}
|
|
else if (!(myImage->flags & IMG_NOTLOADED))
|
|
{
|
|
/* If this is merely a placeholder, there may not be any image to delete */
|
|
x_DisposeImage(myImage, FALSE);
|
|
myImage->width = myImage->height = 0;
|
|
myImage->flags = IMG_NOTLOADED;
|
|
/* Note that myImage->srcURL is carefully left intact. */
|
|
|
|
bSomethingNuked = TRUE;
|
|
|
|
if (bNukeDCache && gPrefs.bEnableDiskCache)
|
|
FlushDCacheEntry(myImage->actualURL);
|
|
}
|
|
|
|
#ifdef FEATURE_CLIENT_IMAGEMAP
|
|
if (bNukeMaps && anElement->lFlags & ELEFLAG_USEMAP)
|
|
{
|
|
Map_Unload(anElement->myMap);
|
|
}
|
|
#endif
|
|
return bSomethingNuked;
|
|
}
|
|
|
|
/* Remove all images in a document and replace them with placeholders */
|
|
BOOL Image_NukeImages(struct _www *pdoc, BOOL bNukeMaps, BOOL bNukeDCache)
|
|
{
|
|
int n;
|
|
BOOL bSomethingNuked = FALSE;
|
|
|
|
for (n = 0; n < pdoc->elementCount; n++)
|
|
{
|
|
if ((pdoc->aElements[n].type == ELE_IMAGE || pdoc->aElements[n].type == ELE_FORMIMAGE))
|
|
if (Image_Nuke(pdoc,n,bNukeMaps,bNukeDCache))
|
|
bSomethingNuked = TRUE;
|
|
}
|
|
return bSomethingNuked;
|
|
}
|
|
|