netscape-revival
lib/libimg/ilclient.c
/* -*- Mode: C; tab-width: 4 -*-
* ilclient.c --- Management of imagelib client data structures,
* including image cache.
*
* Copyright © 1995 Netscape Communications Corporation, all rights reserved.
* $Id: ilclient.c,v 1.55.4.1 1996/09/24 21:16:49 lwei Exp $
*/
#include "xp.h"
#include "if.h"
extern int MK_OUT_OF_MEMORY;
/* for XP_GetString() */
#include "xpgetstr.h"
extern int XP_MSG_IMAGE_NOT_FOUND;
extern int XP_MSG_XBIT_COLOR;
extern int XP_MSG_1BIT_MONO;
extern int XP_MSG_XBIT_GREYSCALE;
extern int XP_MSG_XBIT_RGB;
extern int XP_MSG_DECODED_SIZE;
extern int XP_MSG_WIDTH_HEIGHT;
extern int XP_MSG_SCALED_FROM;
extern int XP_MSG_IMAGE_DIM;
extern int XP_MSG_COLOR;
extern int XP_MSG_NB_COLORS;
extern int XP_MSG_NONE;
extern int XP_MSG_COLORMAP;
extern int XP_MSG_BCKDRP_VISIBLE;
extern int XP_MSG_SOLID_BKGND;
extern int XP_MSG_JUST_NO;
extern int XP_MSG_TRANSPARENCY;
extern int XP_MSG_COMMENT;
extern int XP_MSG_UNKNOWN;
extern int XP_MSG_COMPRESS_REMOVE;
static uint32 image_cache_size;
static int il_cache_trace = FALSE;
/* simple list, in use order */
struct il_cache_struct {
il_container *head;
il_container *tail;
int32 bytes;
int32 max_bytes;
int items;
};
struct il_cache_struct il_cache;
void
IL_SetImageCacheSize(uint32 new_size)
{
image_cache_size = new_size;
IL_ReduceImageCacheSizeTo(new_size);
}
Bool
il_disconnect_container_from_context(MWContext *cx,
il_container *ic_to_be_deleted)
{
il_container_list *ic_list = cx->imageList;
if (!ic_list)
return FALSE;
if (ic_list->ic == ic_to_be_deleted)
{
cx->imageList = ic_list->next;
XP_FREE(ic_list);
}
else
{
for (; ic_list; ic_list = ic_list->next)
{
if (ic_list->next && (ic_list->next->ic == ic_to_be_deleted))
{
il_container_list *to_be_freed = ic_list->next;
ic_list->next = ic_list->next->next;
XP_FREE(to_be_freed);
break;
}
}
}
if (ic_list)
{
cx->images--;
cx->colorSpace->unique_images--;
return TRUE;
}
return FALSE;
}
/* Returns TRUE if image container appears to match search parameters */
static int
il_image_match(il_container *ic, /* candidate for match */
const char *image_url,
IL_IRGB *background_color,
Bool mask_required,
int depth, /* Color depth */
int width, /* *Displayed* image width and height */
int height)
{
/* Width and height both zero indicate usage of image's
natural size. If only one is zero, it indicates scaling
maintaining the image's aspect ratio. */
if (width != ic->layout_width) {
if (! width) return FALSE;
if (width != ic->image->width) return FALSE;
}
if (height != ic->layout_height) {
if (! height) return FALSE;
if (height != ic->image->height) return FALSE;
}
/* Now check background color (in case we remap transparent pixels) */
if (ic->image->transparent) {
if (mask_required) {
if (!ic->image->has_mask)
return FALSE;
} else {
if (ic->image->has_mask)
return FALSE;
if (!background_color)
return FALSE;
if (background_color->red != ic->image->transparent->red)
return FALSE;
if (background_color->green != ic->image->transparent->green)
return FALSE;
if (background_color->blue != ic->image->transparent->blue)
return FALSE;
}
}
/* We use the il_process's depth instead of image container's
* depth, because the latter might not be initialized until
* il_size() has been called.
*/
if (ic->cs->depth != depth)
return FALSE;
if (strcmp(image_url, ic->url_address))
return FALSE;
/* XXX - temporary */
if (ic->rendered_with_custom_palette)
return FALSE;
return TRUE;
}
static int
il_images_match(il_container *ic1, il_container *ic2)
{
return il_image_match(ic1,
ic2->url_address, ic2->image->transparent,
ic2->image->has_mask != 0,
ic2->cs->depth,
ic2->layout_width, ic2->layout_height);
}
static il_container *
il_find_in_cache(uint32 hash,
const char *image_url,
IL_IRGB* background_color,
Bool mask_required,
int depth,
int width,
int height)
{
il_container *ic=0;
XP_ASSERT(hash);
for (ic=il_cache.head; ic; ic=ic->next)
{
if (ic->hash != hash)
continue;
if (il_image_match(ic, image_url,background_color, mask_required,
depth, width, height))
break;
}
if (ic)
{
ILTRACE(2,("il: found ic=0x%08x in cache\n", ic));
return ic;
}
if (il_cache_trace) {
char buffer[1024];
MWContext *alert_context =
XP_FindContextOfType(NULL, MWContextBrowser);
XP_SPRINTF(buffer,
XP_GetString(XP_MSG_IMAGE_NOT_FOUND),
image_url);
if (alert_context)
FE_Alert(alert_context, buffer);
}
return NULL;
}
static void
il_addtocache(il_container *ic);
il_container *
il_get_container(MWContext *cx,
NET_ReloadMethod cache_reload_policy,
const char *image_url,
const char *lowres_image_url,
IL_IRGB *background_color,
IL_DitherMode dither_mode,
Bool mask_required,
int depth,
int layout_width, /* Layout-defined image width and height */
int layout_height)
{
uint32 urlhash, hash;
il_container *ic;
urlhash = hash = il_hash(image_url);
/* and lowsrc */
if (lowres_image_url)
hash += il_hash(lowres_image_url);
/*
* Take into account whether color rendering preferences have
* changed since the image was cached.
*/
hash += 21117 * (int)dither_mode;
/* Check the cache */
ic = il_find_in_cache(hash, image_url, background_color, mask_required,
depth, layout_width, layout_height);
if (ic) {
/* We already started to discard this image container. Make a new one.*/
if ((ic->state == IC_ABORT_PENDING))
ic = NULL;
/* Check if we have to reload or if there's an expiration date */
/* and we've expired or if this is a JavaScript generated image. */
/* We don't want to cache JavaScript generated images because: */
/* 1) The assumption is that they are generated and might change */
/* if the page is reloaded. */
/* 2) Their namespace crosses document boundaries, so caching */
/* could result in incorrect behavior. */
else if ((FORCE_RELOAD(cache_reload_policy) && !ic->forced) ||
(cache_reload_policy != NET_CACHE_ONLY_RELOAD &&
ic->expires && (time(NULL) > ic->expires)) ||
(NET_URL_Type(ic->url_address) == MOCHA_TYPE_URL))
{
/* Get rid of the old copy of the image that we're replacing. */
if (!ic->is_in_use)
{
il_removefromcache(ic);
il_delete_container(ic, TRUE);
}
ic = NULL;
}
/* Printer contexts and window contexts may have different
native image formats, so don't reuse an image cache entry
created for an onscreen context in a printer context or
vice-versa. */
else if (((cx->type == MWContextPrint) ||
(cx->type ==MWContextPostScript)) &&
(ic->context_type != cx->type))
ic = NULL;
}
/* Reorder the cache list so it remains in LRU order*/
if (ic)
il_removefromcache(ic);
/* There's no existing matching container. Make a new one. */
if (!ic) {
ic = XP_NEW_ZAP(il_container);
if (!ic)
return NULL;
if (!(ic->image = XP_NEW_ZAP(IL_Image))) {
XP_FREE(ic);
return NULL;
}
ILTRACE(2, ("il: create ic=0x%08x\n", ic));
ic->hash = hash;
ic->urlhash = urlhash;
ic->url_address = XP_STRDUP(image_url);
ic->ip = cx->imageProcess;
ic->layout_width = layout_width;
ic->layout_height = layout_height;
ic->image->has_mask = mask_required;
ic->context_type = cx->type;
}
il_addtocache(ic);
ic->is_in_use = TRUE;
return ic;
}
void
il_scour_container(il_container *ic)
{
/* scour quantize, ds, scalerow etc */
if (ic->cs->mode == ilCI)
il_free_quantize(ic);
FREE_IF_NOT_NULL(ic->hiurl);
FREE_IF_NOT_NULL(ic->scalerow);
FREE_IF_NOT_NULL(ic->indirect_map);
/* reset a bunch of stuff */
ic->url = NULL;
ic->stream = NULL;
ic->cx = NULL;
ic->forced = FALSE;
ic->is_visible_on_screen = FALSE;
ic->is_backdrop = FALSE;
ic->is_alone = FALSE;
}
/*
* destroy an il_container, freeing it and the image
*/
void
il_delete_container(il_container *ic, int explicitly)
{
XP_ASSERT(ic);
if (ic)
{
ILTRACE(2,("il: delete ic=0x%08x", ic));
/*
* We can't throw away this container until the netlib
* stops sending us stuff. We defer the actual deletion
* of the container until then.
*/
if (IMAGE_CONTAINER_LOADING(ic)) {
ic->state = IC_ABORT_PENDING;
return;
}
XP_ASSERT(ic->clients == NULL);
il_scour_container(ic);
/* delete the image */
XP_ASSERT(ic->image);
if (!ic->image)
return;
FREE_IF_NOT_NULL(ic->image->map);
FREE_IF_NOT_NULL(ic->image->transparent);
FREE_IF_NOT_NULL(ic->comment);
/* bits & mask belongs to the FE */
if (explicitly)
FE_ImageDelete(ic->image);
FREE_IF_NOT_NULL(ic->url_address);
XP_FREE(ic->image);
ic->image = 0;
XP_FREE(ic);
}
}
static char *
il_visual_info(il_container *ic)
{
char *msg = (char *)XP_CALLOC(1, 50);
il_colorspace *cs = ic->cs;
if (!msg)
return NULL;
switch (cs->mode)
{
case ilCI:
XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_COLOR), cs->depth); /* #### i18n */
break;
case ilGrey:
if (cs->depth == 1)
XP_SPRINTF(msg, XP_GetString(XP_MSG_1BIT_MONO)); /* #### i18n */
else
XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_GREYSCALE), cs->depth); /* #### i18n */
break;
case ilRGB:
XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_RGB), cs->depth); /* #### i18n */
break;
default:
XP_ASSERT(0);
*msg=0;
break;
}
return msg;
}
/* Define some macros to help us output HTML */
#define CELL_TOP \
StrAllocCat(output, \
"<TR><TD VALIGN=BASELINE ALIGN=RIGHT><B>");
#define CELL_TITLE(title) \
StrAllocCat(output, title);
#define CELL_MIDDLE \
StrAllocCat(output, \
"</B></TD>" \
"<TD>");
#define CELL_BODY(body) \
StrAllocCat(output, body);
#define CELL_END \
StrAllocCat(output, \
"</TD></TR>");
#define ADD_CELL(c_title, c_body) \
CELL_TOP; \
CELL_TITLE(c_title); \
CELL_MIDDLE; \
CELL_BODY(c_body); \
CELL_END;
static char *
il_HTML_image_info(il_container *ic, int long_form, int show_comment)
{
char tmpbuf[512]; /* Temporary consing area */
char *output = NULL;
IL_Image *image = ic->image;
XP_SPRINTF(tmpbuf, "%lu", (long)image->widthBytes * image->height +
sizeof(il_container));
ADD_CELL(XP_GetString(XP_MSG_DECODED_SIZE), tmpbuf); /* #### i18n */
/* #### i18n */
XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_WIDTH_HEIGHT), image->width, image->height);
if ((image->width != ic->unscaled_width) ||
(image->height != ic->unscaled_height))
{
/* #### i18n */
XP_SPRINTF(tmpbuf + strlen(tmpbuf), XP_GetString(XP_MSG_SCALED_FROM),
ic->unscaled_width, ic->unscaled_height);
}
/* #### i18n */
ADD_CELL(XP_GetString(XP_MSG_IMAGE_DIM), tmpbuf);
if (long_form) {
char *visual_info = il_visual_info(ic);
if (visual_info) {
ADD_CELL(XP_GetString(XP_MSG_COLOR), visual_info); /* #### i18n */
XP_FREE(visual_info);
}
if (image->map)
XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_NB_COLORS), image->unique_colors); /* #### i18n */
else
XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_NONE)); /* #### i18n */
ADD_CELL(XP_GetString(XP_MSG_COLORMAP), tmpbuf);
if (image->transparent) {
if (image->has_mask)
XP_SPRINTF(tmpbuf,
/* #### i18n */
XP_GetString(XP_MSG_BCKDRP_VISIBLE));
else
XP_SPRINTF(tmpbuf,
/* #### i18n */
XP_GetString(XP_MSG_SOLID_BKGND),
image->transparent->red,
image->transparent->green,
image->transparent->blue);
} else {
XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_JUST_NO)); /* #### i18n */
}
ADD_CELL(XP_GetString(XP_MSG_TRANSPARENCY), tmpbuf); /* #### i18n */
}
if (show_comment && ic->comment) {
XP_SPRINTF(tmpbuf, "%.500s", ic->comment);
ADD_CELL(XP_GetString(XP_MSG_COMMENT), tmpbuf); /* #### i18n */
}
return output;
}
char *
IL_HTMLImageInfo(char *url_address)
{
il_container *ic;
char *output = NULL;
char *il_msg;
for (ic=il_cache.head; ic; ic=ic->next)
{
if (!strcmp(ic->url_address, url_address))
break;
}
if ((ic == NULL) || (ic->state != IC_COMPLETE))
return NULL;
il_msg = il_HTML_image_info(ic, TRUE, TRUE);
if (il_msg == NULL)
return NULL;
StrAllocCat(output,
"<TABLE CELLSPACING=0 CELLPADDING=1 "
"BORDER=0 ALIGN=LEFT WIDTH=66%>");
StrAllocCat(output, il_msg);
StrAllocCat(output, "</TABLE> <A HREF=\"");
StrAllocCat(output, url_address);
StrAllocCat(output, "\"> <IMG WIDTH=90% ALIGN=CENTER SRC=\"");
StrAllocCat(output, url_address);
StrAllocCat(output, "\"></A>\n");
XP_FREE(il_msg);
return output;
}
/*
* Create an HTML stream and generate HTML describing
* the image cache. Use "about:memory-cache" URL to acess.
*/
int
IL_DisplayMemCacheInfoAsHTML(FO_Present_Types format_out,
URL_Struct *urls,
MWContext *cx)
{
char buffer[2048];
char *output = NULL;
char *img_info;
char *address;
char *escaped;
NET_StreamClass * stream;
Bool long_form = FALSE;
int status;
if(strcasestr(urls->address, "?long"))
long_form = TRUE;
else if(strcasestr(urls->address, "?traceon"))
il_cache_trace = TRUE;
else if(strcasestr(urls->address, "?traceoff"))
il_cache_trace = FALSE;
StrAllocCopy(urls->content_type, TEXT_HTML);
format_out = CLEAR_CACHE_BIT(format_out);
stream = NET_StreamBuilder(format_out, urls, cx);
if (!stream)
return 0;
/*
* Define a macro to push a string up the stream
* and handle errors
*/
#define PUT_PART(part) \
{ \
status = (*stream->put_block)(stream->data_object, \
part ? part : "Unknown", \
part ? XP_STRLEN(part) : 7); \
if (status < 0) \
goto END; \
}
/* Send something to the screen immediately. This will force all
* the images on the page to enter the cache now. Otherwise,
* layout will be causing cache state to be modified even as we're
* trying to display it.
*/
XP_STRCPY(buffer,
"<TITLE>Information about the Netscape image cache</TITLE>\n");
PUT_PART(buffer);
XP_SPRINTF(buffer,
"<h2>Image Cache statistics</h2>\n"
"<TABLE CELLSPACING=0 CELLPADDING=1>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Maximum size:</b></TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Current size:</b></TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Number of images in cache:</b></TD>\n"
"<TD>%d</TD>\n"
"</TR>\n"
"<TR>\n"
"<TD ALIGN=RIGHT><b>Average cached image size:</b></TD>\n"
"<TD>%ld</TD>\n"
"</TR>\n"
"</TABLE>\n"
"<HR>",
(long)il_cache.max_bytes,
(long)il_cache.bytes,
il_cache.items,
(il_cache.bytes ?
(long)il_cache.bytes / il_cache.items : 0L));
PUT_PART(buffer);
{
il_container *ic, *l = NULL;
IL_Image *image;
for (ic=il_cache.head; ic; ic=ic->next) {
image = ic->image;
/* Sanity check */
if (l)
XP_ASSERT(ic->prev == l);
l = ic;
/* Don't display uninteresting images */
if ((ic->state != IC_COMPLETE) &&
(ic->state != IC_SIZED) &&
(ic->state != IC_HIRES) &&
(ic->state != IC_MULTI))
continue;
StrAllocCat(output, "<TABLE>\n");
/* Emit DocInfo link to URL */
address = ic->url_address;
XP_STRCPY(buffer, "<A TARGET=Internal_URL_Info HREF=about:");
XP_STRCAT(buffer, address);
XP_STRCAT(buffer, ">");
escaped = NET_EscapeHTML(address);
XP_STRCAT(buffer, escaped);
XP_FREE(escaped);
XP_STRCAT(buffer, "</A>");
ADD_CELL("URL:", buffer);
/* Rest of image info (size, colors, depth, etc.) */
img_info = il_HTML_image_info(ic, long_form, FALSE);
if (img_info) {
StrAllocCat(output, img_info);
XP_FREE(img_info);
}
StrAllocCat(output, "</TABLE><P>\n");
}
if (output)
PUT_PART(output);
}
END:
if (output)
XP_FREE(output);
if(status < 0)
(*stream->abort)(stream->data_object, status);
else
(*stream->complete)(stream->data_object);
return 1;
}
il_container *
il_removefromcache(il_container *ic)
{
int32 image_bytes;
XP_ASSERT(ic);
if (ic)
{
ILTRACE(2,("il: remove ic=0x%08x from cache\n", ic));
XP_ASSERT(ic->next || ic->prev || (il_cache.head == il_cache.tail));
/* Remove entry from doubly-linked list. */
if (il_cache.head == ic)
il_cache.head = ic->next;
if (il_cache.tail == ic)
il_cache.tail = ic->prev;
if (ic->next)
ic->next->prev = ic->prev;
if (ic->prev)
ic->prev->next = ic->next;
ic->next = ic->prev = NULL;
image_bytes = ((int32) ic->image->widthBytes) * ic->image->height;
XP_ASSERT (il_cache.bytes >= (int32)image_bytes);
if (il_cache.bytes < (int32)image_bytes)
il_cache.bytes = 0;
else
il_cache.bytes -= image_bytes;
il_cache.items--;
XP_ASSERT(il_cache.items >= 0);
}
return ic;
}
void
il_adjust_cache_fullness(int32 bytes)
{
il_cache.bytes += bytes;
XP_ASSERT(il_cache.bytes >= 0);
/* Safety net - This should never happen. */
if (il_cache.bytes < 0)
il_cache.bytes = 0;
/* We get all of the memory cache. (Only images are cached in memory.) */
IL_ReduceImageCacheSizeTo(image_cache_size);
/* Collect some debugging info. */
if (il_cache.bytes > il_cache.max_bytes)
il_cache.max_bytes = il_cache.bytes;
}
static void
il_addtocache(il_container *ic)
{
ILTRACE(2,("il: add ic=0x%08x to cache\n", ic));
XP_ASSERT(!ic->prev && !ic->next);
/* Thread onto doubly-linked list, kept in LRU order */
ic->prev = NULL;
ic->next = il_cache.head;
if (il_cache.head)
il_cache.head->prev = ic;
else {
XP_ASSERT(il_cache.tail == NULL);
il_cache.tail = ic;
}
il_cache.head = ic;
/* Image storage is added in when image is sized */
if (ic->sized)
il_cache.bytes += (uint32)ic->image->widthBytes * ic->image->height;
il_cache.items++;
}
void
IL_ReduceImageCacheSizeTo(uint32 new_size)
{
int32 last_size = 0;
while ((il_cache.bytes > (int32)new_size) && (il_cache.bytes != last_size)) {
last_size = il_cache.bytes;
IL_ShrinkCache();
}
}
uint32
IL_ShrinkCache(void)
{
il_container *ic;
ILTRACE(3,("shrink"));
for (ic = il_cache.tail; ic; ic = ic->prev)
{
if (ic->is_user_backdrop || ic->is_in_use)
continue;
if (il_cache_trace) {
char buffer[1024];
MWContext *alert_context =
XP_FindContextOfType(NULL, MWContextBrowser);
sprintf(buffer,
XP_GetString(XP_MSG_COMPRESS_REMOVE), ic->url_address);
if (alert_context)
FE_Alert(alert_context, buffer);
}
il_removefromcache(ic);
il_delete_container(ic, TRUE);
break;
}
return il_cache.bytes;
}
uint32 IL_GetCacheSize()
{
return il_cache.bytes;
}
static int
il_replace_client(MWContext *cx, il_container *ic,
void* new_client, void* old_client)
{
int num_replacements = 0;
il_client *c;
if (!ic)
return 0;
c = ic->clients;
while (c)
{
if (c->client == old_client)
{
c->client = new_client;
num_replacements++;
}
c = c->next;
}
return num_replacements;
}
void
IL_UnCache(IL_Image *img)
{
il_container *ic;
if (img)
{
for (ic=il_cache.head; ic; ic=ic->next)
{
if ((ic->image == img) && !ic->is_in_use)
{
il_removefromcache(ic);
il_delete_container(ic, TRUE);
return;
}
}
}
}
int
il_add_client(MWContext *cx,
il_container *ic,
void* client,
int is_view_image)
{
il_client *c;
il_container_list *ic_list;
if (!(c = XP_NEW_ZAP(il_client)))
{
ILTRACE(0,("il: MEM il_add_client"));
return MK_OUT_OF_MEMORY;
}
c->client = client;
c->cx = cx;
c->is_view_image = is_view_image;
/* added to the end */
if (!ic->lclient)
ic->clients = c;
else
ic->lclient->next = c;
ic->lclient = c;
ILTRACE(3, ("il: add_client ic=0x%08x, client =0x%08x\n",
ic, client));
/* Add to the list of images for this context,
* but only one entry per unique image.
*/
il_disconnect_container_from_context(cx, ic);
ic_list = XP_NEW_ZAP(il_container_list);
if (!ic_list)
{
ILTRACE(0,("il: MEM il_add_client"));
return MK_OUT_OF_MEMORY;
}
ic_list->ic = ic;
ic_list->next = cx->imageList;
cx->imageList = ic_list;
cx->images++;
cx->colorSpace->unique_images++;
return 0;
}
/* Delete an LO_ImageStruct from the list of clients for an image container.
* Return TRUE if successful, FALSE otherwise.
*/
static Bool
il_delete_client(il_container *ic, void *client)
{
MWContext *cx;
il_client *c;
il_client *lastc = NULL;
c = ic->clients;
while (c) {
if (c->client == client)
break;
lastc = c;
c = c->next;
}
if (!c)
return FALSE;
if (c == ic->clients)
ic->clients = c->next;
else
lastc->next = c->next;
if (c == ic->lclient)
ic->lclient = lastc;
cx = c->cx;
XP_FREE(c);
/* Decrement the number of unique images for this client's
* context, but be careful to do this only when there are no other
* clients of this image with the same context.
*/
c = ic->clients;
while (c) {
if (c->cx == cx)
break;
c = c->next;
}
if (!c) {
il_disconnect_container_from_context(cx, ic);
/* Images need "home" contexts on which to perform some operations, such as network activity */
if (ic->clients) {
if (ic->cx == cx)
ic->cx = ic->clients->cx;
ic->cs = ic->clients->cx->colorSpace;
#ifdef GOLD
/* This should be done for all versions of the navigator.
* However, it's only a critical bug for Gold, and the
* fix was made very late in the Akbar cycle. So, for
* reasons of paranoia, we only made the fix for the
* Gold source code. In Dogbert, we should remove the
* #ifdef, and always do this assignment.
*/
ic->ip = ic->clients->cx->imageProcess;
#endif
}
}
ILTRACE(3, ("il: delete_client ic=0x%08x, client =0x%08x\n", ic, client));
return TRUE;
}
static void
il_delete_all_clients(il_container *ic)
{
il_client *c;
il_client *nextc = NULL;
for (c = ic->clients; c; c = nextc)
{
nextc = c->next;
il_delete_client(ic, c->client);
}
}
/* Used when exiting the client, this code frees all imagelib data structures.
* This is done for two reasons:
* 1) It makes leakage analysis of the heap easier, and
* 2) It causes resources to be freed on Windows that would otherwise persist
* beyond the browser's lifetime.
*
* XXX - Actually, right now it just frees the cached images. There are still
* a couple of other places that leaks need to be fixed.
*/
void
IL_Shutdown()
{
il_container *ic, *ic_next;
for (ic = il_cache.head; ic; ic = ic_next)
{
ic_next = ic->next;
il_delete_all_clients(ic);
il_removefromcache(ic);
il_delete_container(ic, TRUE);
}
XP_ASSERT(il_cache.bytes == 0);
}
/* Disconnect a layout image client from the imagelib's image container
* structure. If the last client is removed, the container can be reclaimed,
* either by storing it in the cache or throwing it away.
* Always returns FALSE.
*/
Bool
IL_FreeImage(MWContext *cx,
IL_Image *image,
LO_ImageStruct* lo_image)
{
Bool client_deleted;
il_container *ic, *ic2, *ic2_next;
il_container_list *ic_list = cx->imageList;
while (ic_list) {
ic = ic_list->ic;
if (ic->image == image)
break;
ic_list = ic_list->next;
}
XP_ASSERT(ic_list);
ILTRACE(1, ("il: IL_FreeImage: ic = 0x%08x\n", ic));
if (!ic_list)
return FALSE;
client_deleted = il_delete_client(ic, lo_image);
XP_ASSERT(client_deleted);
/* The image container can't be deleted until all clients are done. */
if (ic->clients)
return FALSE;
/* Don't allow any new streams to be created in
il_image_complete() as a result of a looping animation. (It's
possible that this image is being freed before the stream
completes/aborts. This can happen in autoscroll mode and the
editor apparently allows this to occur also.) */
ic->loop_count = 0;
il_image_abort(ic);
XP_ASSERT(cx->images >= 0);
if (!cx->images)
{
XP_ASSERT(!cx->imageList);
cx->imageList = 0;
if (cx->imageProcess)
{
cx->imageProcess->active = 0;
}
}
if ((ic->state != IC_COMPLETE) || ic->multi || ic->rendered_with_custom_palette ||
(NET_URL_Type(ic->url_address) == MOCHA_TYPE_URL))
{
il_removefromcache(ic);
il_delete_container(ic, TRUE);
return FALSE;
}
/* Don't allow duplicate entries in cache */
for (ic2 = il_cache.head; ic2; ic2 = ic2_next)
{
ic2_next = ic2->next;
if ((!ic2->is_in_use) && il_images_match(ic, ic2)) {
il_removefromcache(ic2);
il_delete_container(ic2, TRUE);
}
}
ic->is_in_use = FALSE;
return FALSE;
}
/*
* Replace all occurances of this old image element with the
* new element passed in. The old element is going to be freed by
* layout, but it wants to keep the image around, so it is hanging it
* off of a new element.
*/
Bool
IL_ReplaceImage(MWContext *cx,
LO_ImageStruct* new_lo_image,
LO_ImageStruct* old_lo_image)
{
int ret;
il_container_list *ic_list;
ret = 0;
ic_list = cx->imageList;
while (ic_list)
{
ret += il_replace_client(cx, ic_list->ic,
(void*)new_lo_image,
(void*)old_lo_image);
ic_list = ic_list->next;
}
XP_ASSERT(ret == 1);
if (!ret)
return TRUE;
else
return FALSE;
}
/* This routine is a "safety net", in case layout didn't free up all the
* images on a page. It assumes layout made a mistake and frees them anyway.
*/
void
IL_StartPage(MWContext *cx)
{
il_client *c, *nextc;
il_container *ic;
il_container_list *ic_list, *ic_list_next;
if (cx->images > 0) {
XP_ASSERT(cx->imageList);
XP_TRACE(("Hey, layout failed to free all the images on this page!\n"
"There are still %d images unfreed.\n",
cx->images));
for (ic_list = cx->imageList; ic_list; ic_list = ic_list_next)
{
ic_list_next = ic_list->next;
ic = ic_list->ic;
for (c = ic->clients; c; c = nextc)
{
nextc = c->next;
if (c->cx == cx)
IL_FreeImage(c->cx, ic->image, (LO_ImageStruct*)c->client);
}
}
XP_ASSERT(cx->images == 0);
XP_ASSERT(cx->imageList == NULL);
}
if (cx->colorSpace) {
cx->colorSpace->install_colormap_allowed = FALSE;
cx->colorSpace->install_colormap_forbidden = FALSE;
}
ILTRACE(1, ("il: IL_StartPage\n"));
}