home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / modules / libimg / src / ilclient.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  45.0 KB  |  1,424 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* -*- Mode: C; tab-width: 4 -*-
  20.  *   ilclient.c --- Management of imagelib client data structures,
  21.  *                  including image cache.
  22.  *
  23.  *   $Id: ilclient.c,v 3.1 1998/03/28 03:35:02 ltabb Exp $
  24.  */
  25.  
  26.  
  27. #include "if.h"
  28. #include "il_strm.h"            /* For OPAQUE_CONTEXT. */
  29.  
  30. extern int MK_OUT_OF_MEMORY;
  31.  
  32. /* for XP_GetString() */
  33. #include "xpgetstr.h"
  34.  
  35. extern int XP_MSG_IMAGE_NOT_FOUND;
  36. extern int XP_MSG_XBIT_COLOR;    
  37. extern int XP_MSG_1BIT_MONO;
  38. extern int XP_MSG_XBIT_GREYSCALE;
  39. extern int XP_MSG_XBIT_RGB;
  40. extern int XP_MSG_DECODED_SIZE;    
  41. extern int XP_MSG_WIDTH_HEIGHT;    
  42. extern int XP_MSG_SCALED_FROM;    
  43. extern int XP_MSG_IMAGE_DIM;    
  44. extern int XP_MSG_COLOR;    
  45. extern int XP_MSG_NB_COLORS;    
  46. extern int XP_MSG_NONE;    
  47. extern int XP_MSG_COLORMAP;    
  48. extern int XP_MSG_BCKDRP_VISIBLE;    
  49. extern int XP_MSG_SOLID_BKGND;    
  50. extern int XP_MSG_JUST_NO;    
  51. extern int XP_MSG_TRANSPARENCY;    
  52. extern int XP_MSG_COMMENT;    
  53. extern int XP_MSG_UNKNOWN;    
  54. extern int XP_MSG_COMPRESS_REMOVE;    
  55.  
  56. static uint32 image_cache_size;
  57.  
  58. #ifndef M12N                    /* XXXM12N Cache trace: cleanup/eliminate. */
  59. static int il_cache_trace = FALSE; /* XXXM12N Clean up/eliminate */
  60. #endif
  61.  
  62. /* simple list, in use order */
  63. struct il_cache_struct {
  64.     il_container *head;
  65.     il_container *tail;
  66.     int32 bytes;
  67.     int32 max_bytes;
  68.     int items;
  69. };
  70.  
  71. struct il_cache_struct il_cache;
  72.  
  73. /* Add an image container to a context's container list. */
  74. static PRBool
  75. il_add_container_to_context(IL_GroupContext *img_cx, il_container *ic)
  76. {
  77.     il_container_list *ic_list;
  78.     
  79.     ic_list = XP_NEW_ZAP(il_container_list);
  80.     if (!ic_list)
  81.         return PR_FALSE;
  82.  
  83.     ic_list->ic = ic;
  84.     ic_list->next = img_cx->container_list;
  85.     img_cx->container_list = ic_list;
  86.     return PR_TRUE;
  87. }
  88.  
  89. /* Remove an image container from a context's container list. */
  90. static PRBool
  91. il_remove_container_from_context(IL_GroupContext *img_cx, il_container *ic)
  92. {
  93.     il_container_list *current, *next;
  94.  
  95.     current = img_cx->container_list;
  96.     if (!current)
  97.         return PR_FALSE;
  98.     
  99.     if (current->ic == ic) {
  100.         img_cx->container_list = current->next;
  101.         XP_FREE(current);
  102.         return PR_TRUE;
  103.     }
  104.     else {
  105.         for (; current; current = next) {
  106.             next = current->next;
  107.             if (next && (next->ic == ic)) {
  108.                 current->next = next->next;
  109.                 XP_FREE(next);
  110.                 return PR_TRUE;
  111.             }
  112.         }
  113.     }
  114.     return PR_FALSE;
  115. }
  116.  
  117. /* Returns PR_TRUE if the context didn't belong to the container's client
  118.    context list, and was successfully added to that list. */
  119. static PRBool
  120. il_add_client_context(IL_GroupContext *img_cx, il_container *ic)
  121. {
  122.     il_context_list *current = ic->img_cx_list;
  123.  
  124.     /* Check whether the context is already in the client context list. */
  125.     if (current)
  126.         for (; current; current = current->next) {
  127.         if (current->img_cx == img_cx)
  128.             return PR_FALSE;
  129.     }
  130.  
  131.     if (il_add_container_to_context(img_cx, ic)) {
  132.         /* If the client image context doesn't already belong to the
  133.            container's client context list, then add it to the beginning
  134.            of the list. */
  135.         current = XP_NEW_ZAP(il_context_list);
  136.         if (!current)
  137.             return PR_FALSE;
  138.         current->img_cx = img_cx;
  139.         current->next = ic->img_cx_list;
  140.         ic->img_cx_list = current;
  141.  
  142.         /* Increment the number of containers in this context.  */
  143.         img_cx->num_containers++;
  144.  
  145.         /* If the image is still loading, increment the number of loading
  146.            containers in the context.  Observer notification takes place
  147.            if the image group previously had no actively loading images. */
  148.         if (IMAGE_CONTAINER_ACTIVE(ic) && !ic->is_looping) {
  149.             XP_ASSERT(!ic->is_aborted);
  150.             if (!img_cx->num_loading)
  151.                 il_group_notify(img_cx, IL_STARTED_LOADING);
  152.             img_cx->num_loading++;
  153.         }
  154.  
  155.         /* If the image is looping, increment the number of looping containers
  156.            in the context.  Observer notification takes place if the image
  157.            group previously had no looping images. */
  158.         if (ic->is_looping) {
  159.             if (!img_cx->num_looping)
  160.                 il_group_notify(img_cx, IL_STARTED_LOOPING);
  161.             img_cx->num_looping++;
  162.         }
  163.  
  164. #ifdef DEBUG_GROUP_OBSERVER
  165.         XP_TRACE(("5 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  166.                   img_cx, img_cx->num_containers, img_cx->num_loading,
  167.                   img_cx->num_looping, img_cx->num_aborted));
  168. #endif /* DEBUG_GROUP_OBSERVER */
  169.  
  170.         return PR_TRUE;
  171.     }
  172.     else {
  173.         return PR_FALSE;
  174.     }
  175. }
  176.  
  177. /* Returns PR_TRUE if the client context belonged to the container's client
  178.    context list, and was successfully deleted from that list. */
  179. static PRBool
  180. il_remove_client_context(IL_GroupContext *img_cx, il_container *ic)
  181. {
  182.     PRBool deleted_element = PR_FALSE;
  183.     il_context_list *current, *next;
  184.  
  185.     current = ic->img_cx_list;
  186.     if (!current)
  187.         return PR_FALSE;
  188.  
  189.     if (current->img_cx == img_cx) {
  190.         ic->img_cx_list = current->next;
  191.         XP_FREE(current);
  192.         deleted_element = PR_TRUE;
  193.     }
  194.     else {
  195.         for (; current; current = next) {
  196.             next = current->next;
  197.             if (next && (next->img_cx == img_cx)) {
  198.                 current->next = next->next;
  199.                 XP_FREE(next);
  200.                 deleted_element = PR_TRUE;
  201.                 break;
  202.             }
  203.         }
  204.     }
  205.  
  206.     if (deleted_element && il_remove_container_from_context(img_cx, ic)) {
  207.         /* Decrement the number of containers in this context. */
  208.         img_cx->num_containers--;
  209.  
  210.         /* If the image didn't load successfully, then decrement the number
  211.            of loading containers in the context.  (If the image did load
  212.            successfully, the number of loading containers in the context was
  213.            previously decremented at the time the container loaded, so don't
  214.            do it again.)  Observer notification takes place if this was
  215.            the last image loading in the group. */
  216.         if (!(ic->state == IC_COMPLETE || ic->is_looping)) {
  217.             img_cx->num_loading--;
  218.             if (!img_cx->num_loading)
  219.                 il_group_notify(img_cx, IL_FINISHED_LOADING);
  220.  
  221.             if (ic->is_aborted)           
  222.                 img_cx->num_aborted--;
  223.         }
  224.  
  225.         /* If the image was looping, then decrement the number of looping
  226.            containers in the context.  Observer notification takes place if
  227.            this was the last looping image in the group. */
  228.         if (ic->is_looping) {
  229.             img_cx->num_looping--;
  230.             if (!img_cx->num_looping)
  231.                 il_group_notify(img_cx, IL_FINISHED_LOOPING);
  232.         }
  233.  
  234. #ifdef DEBUG_GROUP_OBSERVER
  235.         XP_TRACE(("6 img_cx=%x total=%d loading=%d looping=%d aborted=%d",
  236.                   img_cx, img_cx->num_containers, img_cx->num_loading,
  237.                   img_cx->num_looping, img_cx->num_aborted));
  238. #endif /* DEBUG_GROUP_OBSERVER */
  239.  
  240.         return PR_TRUE;
  241.     }
  242.     else {
  243.         return PR_FALSE;
  244.     }
  245. }
  246.  
  247. /* Returns TRUE if image container appears to match search parameters */
  248. static int
  249. il_image_match(il_container *ic,          /* Candidate for match. */
  250.                IL_DisplayType display_type,
  251.                const char *image_url,
  252.                IL_IRGB *background_color,
  253.                int req_depth,             /* Colorspace depth. */
  254.                int req_width,             /* Target image width. */
  255.                int req_height)            /* Target image height. */
  256. {
  257.     PRBool ic_sized = (ic->state >= IC_SIZED);
  258.     NI_PixmapHeader *img_header = &ic->image->header;
  259.     IL_IRGB *img_trans_pixel = img_header->transparent_pixel;
  260.  
  261.     /* Allowable conditions for there to be a size match.  req_width and
  262.        req_height both zero indicate usage of the image's natural size.
  263.        If only one is zero, it indicates scaling maintaining the image's
  264.        aspect ratio.  If ic_size has been called for the image cache entry,
  265.        then ic->dest_width and ic->dest_height represent the size of the
  266.        image as it will be displayed.  If ic_size has not been called for
  267.        the cache entry, then ic->dest_width and ic->dest_height represent the
  268.        requested size. */
  269.     if (!(
  270.         /* Both dimensions match (either zero is a match.) */
  271.         ((req_width == ic->dest_width) && (req_height == ic->dest_height)) ||
  272.         /* Width matches, request height zero, aspect ratio same. */
  273.         (ic_sized && (req_width == ic->dest_width) && !req_height &&
  274.          !ic->aspect_distorted) ||
  275.         /* Height matches, request width zero, aspect ratio same. */
  276.         (ic_sized && (req_height == ic->dest_height) && !req_width &&
  277.          !ic->aspect_distorted) ||
  278.         /* Request dimensions zero, cache entry has natural dimensions. */
  279.         (!req_width && !req_height && ic->natural_size)
  280.         ))
  281.         return FALSE;
  282.  
  283.     /* We allow any depth image through as the FE may have asked us to
  284.        decode to a colorspace other than the display colorspace. */
  285.  
  286.     /* Now check the background color.  This only applies to transparent
  287.        images, so skip this test if the candidate for the match is known
  288.        to be opaque. */
  289.     if (!ic_sized || img_trans_pixel) {
  290.         if (background_color) {
  291.             if (ic->background_color) {
  292.                 /* Both request and candidate have a background color; check
  293.                    whether they match. */
  294.                 if (background_color->red != ic->background_color->red)
  295.                     return FALSE;
  296.                 if (background_color->green != ic->background_color->green)
  297.                     return FALSE;
  298.                 if (background_color->blue != ic->background_color->blue)
  299.                     return FALSE;
  300.             }
  301.             else {
  302.                 /* A background color was requested, but the candidate does
  303.                    not have one. */
  304.                 return FALSE;
  305.             }
  306.         }
  307.         else {                      /* No background color was requested. */
  308.             if (ic->background_color) {
  309.                 /* A background color was not requested, but the candidate
  310.                    has one.  This means that while the current request may
  311.                    need a mask, the candidate definitely does not have one. */
  312.                 return FALSE;
  313.             }
  314.             else {
  315.                 /* Neither the request nor the candidate have a background
  316.                    color, so don't disqualify the candidate. */
  317.             }
  318.         }
  319.     }
  320.  
  321.  
  322.     /* Check the url (we have already checked the hash value which is based
  323.        on the url.) */
  324.     if (strcmp(image_url, ic->url_address))
  325.         return FALSE;
  326.  
  327.     /* Printer contexts and window contexts may have different
  328.        native image formats, so don't reuse an image cache entry
  329.        created for an onscreen context in a printer context or
  330.        vice-versa. */
  331.     if (display_type != ic->display_type)
  332.         return FALSE;
  333.  
  334.     /* XXX - temporary */
  335.     if (ic->rendered_with_custom_palette)
  336.         return FALSE;
  337.                 
  338.     return TRUE;
  339. }
  340.  
  341. static int
  342. il_images_match(il_container *ic1, il_container *ic2)
  343. {
  344.     return il_image_match(ic1,
  345.                           ic2->display_type,
  346.                           ic2->url_address,
  347.                           ic2->background_color,
  348.                           ic2->image->header.color_space->pixmap_depth,
  349.                           ic2->dest_width, ic2->dest_height);
  350. }
  351.  
  352. static il_container *
  353. il_find_in_cache(IL_DisplayType display_type,
  354.                  uint32 hash,
  355.                  const char *image_url,
  356.                  IL_IRGB* background_color,
  357.                  int req_depth,
  358.                  int req_width,
  359.                  int req_height)
  360. {
  361.     il_container *ic=0;
  362.     XP_ASSERT(hash);
  363.     for (ic=il_cache.head; ic; ic=ic->next)
  364.     {
  365.         if (ic->hash != hash)
  366.             continue;
  367.         if (il_image_match(ic, display_type, image_url, background_color, req_depth,
  368.                            req_width, req_height))
  369.             break;
  370.     }
  371.     if (ic)
  372.     {
  373.         ILTRACE(2,("il:  found ic=0x%08x in cache\n", ic));
  374.         return ic;
  375.     }
  376.  
  377. #ifndef M12N                    /* XXXM12N Cache trace: cleanup/eliminate. */
  378.     if (il_cache_trace) {
  379.         char buffer[1024];
  380.         MWContext *alert_context =
  381.             XP_FindContextOfType(NULL, MWContextBrowser);
  382.         if(!alert_context)
  383.             alert_context = XP_FindContextOfType(NULL, MWContextPane);
  384.  
  385.         XP_SPRINTF(buffer,
  386.                     XP_GetString(XP_MSG_IMAGE_NOT_FOUND),
  387.                     image_url);
  388.  
  389.  
  390.         if (alert_context)
  391.             FE_Alert(alert_context, buffer);
  392.     }
  393. #endif /* M12N */
  394.     
  395.     return NULL;
  396. }
  397.  
  398. static void
  399. il_addtocache(il_container *ic);
  400.  
  401. il_container *
  402. il_get_container(IL_GroupContext *img_cx,    
  403.                  NET_ReloadMethod cache_reload_policy,
  404.                  const char *image_url,
  405.                  IL_IRGB *background_color,
  406.                  IL_DitherMode dither_mode,
  407.                  int req_depth,
  408.                  int req_width,  /* Target width requested by client. */
  409.                  int req_height) /* Target height requested by client. */
  410. {
  411.     uint32 urlhash, hash;
  412.     il_container *ic;
  413.     JMCException *exc = NULL;
  414.  
  415.     urlhash = hash = il_hash(image_url);
  416.  
  417.     /*
  418.      * Take into account whether color rendering preferences have
  419.      * changed since the image was cached.
  420.      */
  421.     hash += 21117 * (int)dither_mode;
  422.            
  423.     /* Check the cache */
  424.     ic = il_find_in_cache(img_cx->display_type, hash, image_url,
  425.                           background_color, req_depth, req_width, req_height);
  426.     
  427.     if (ic) {
  428.        
  429.         /* We already started to discard this image container.
  430.            Make a new one.*/
  431.         if ((ic->state == IC_ABORT_PENDING))
  432.             ic = NULL;
  433.  
  434.         /* Check if we have to reload or if there's an expiration date   */
  435.         /* and we've expired or if this is a JavaScript generated image. */
  436.         /* We don't want to cache JavaScript generated images because:   */
  437.         /* 1) The assumption is that they are generated and might change */
  438.         /*    if the page is reloaded.                                   */
  439.         /* 2) Their namespace crosses document boundaries, so caching    */
  440.         /*    could result in incorrect behavior.                        */
  441.  
  442.         else if ((FORCE_RELOAD(cache_reload_policy) && !ic->forced) ||
  443.                  (cache_reload_policy != NET_CACHE_ONLY_RELOAD &&
  444.                   ic->expires && (time(NULL) > ic->expires)) ||
  445.                  (NET_URL_Type(ic->url_address) == MOCHA_TYPE_URL))
  446.         {
  447.             /* Get rid of the old copy of the image that we're replacing. */
  448.             if (!ic->is_in_use) 
  449.             {
  450.                 il_removefromcache(ic);
  451.                 il_delete_container(ic);
  452.             }
  453.             ic = NULL;
  454.             
  455.         }
  456.     }
  457.  
  458.     /* Reorder the cache list so it remains in LRU order*/
  459.     if (ic)
  460.         il_removefromcache(ic);
  461.  
  462.     /* There's no existing matching container.  Make a new one. */
  463.     if (!ic) {
  464.         ic = XP_NEW_ZAP(il_container);
  465.         if (!ic)
  466.             return NULL;
  467.  
  468.         /* Allocate the source image header. */
  469.         if (!(ic->src_header = XP_NEW_ZAP(NI_PixmapHeader))) {
  470.             XP_FREE(ic);
  471.             return NULL;
  472.         }
  473.  
  474.         /* Allocate the source image's colorspace.  The fields will be
  475.            filled in by the image decoder. */
  476.         if (!(ic->src_header->color_space = XP_NEW_ZAP(IL_ColorSpace))) {
  477.             XP_FREE(ic->src_header);
  478.             XP_FREE(ic);
  479.             return NULL;
  480.         }
  481.         IL_AddRefToColorSpace(ic->src_header->color_space);
  482.  
  483.         /* Allocate the destination image structure.  A destination mask
  484.            structure will be allocated later if the image is determined to
  485.            be transparent and no background_color has been provided. */
  486.         if (!(ic->image = XP_NEW_ZAP(IL_Pixmap))) {
  487.             IL_ReleaseColorSpace(ic->src_header->color_space);
  488.             XP_FREE(ic->src_header);
  489.             XP_FREE(ic);
  490.             return NULL;
  491.         }
  492.         
  493.         /* Set the destination image colorspace to be the source image's
  494.            colorspace.  The Front Ends can override this with the display
  495.            colorspace. */
  496.         ic->image->header.color_space = ic->src_header->color_space;
  497.         IL_AddRefToColorSpace(ic->image->header.color_space);
  498.  
  499.         /* Save the requested background color in the container. */
  500.         if (background_color) {
  501.             if (!(ic->background_color = XP_NEW_ZAP(IL_IRGB))) {
  502.                 XP_FREE(ic->image);
  503.                 IL_ReleaseColorSpace(ic->src_header->color_space);
  504.                 IL_ReleaseColorSpace(ic->image->header.color_space);
  505.                 XP_FREE(ic->src_header);
  506.                 XP_FREE(ic);
  507.                 return NULL;
  508.             }
  509.             XP_MEMCPY(ic->background_color, background_color, sizeof(IL_IRGB));
  510.         }
  511.         else {
  512.             ic->background_color = NULL;
  513.         }
  514.  
  515.         ILTRACE(2, ("il: create ic=0x%08x\n", ic));
  516.  
  517.         ic->hash = hash;
  518.         ic->urlhash = urlhash;
  519.         ic->url_address = XP_STRDUP(image_url);
  520.         ic->is_url_loading = PR_FALSE;
  521.         ic->dest_width  = req_width;
  522.         ic->dest_height = req_height;
  523.  
  524.         /* The image context is saved for use during decoding only. */
  525.         ic->img_cx = img_cx;
  526.      
  527.         /* The type of display for which this image is being decoded e.g.
  528.            screen, printer, postscript. */
  529.         ic->display_type = img_cx->display_type;
  530.  
  531.         /* Certain callbacks, like DestroyPixmap, can be invoked after the
  532.            image context has been destroyed, so we hold on to the callback
  533.            interface and increment its reference count.  The reference count
  534.            is decremented when the container is destroyed. */
  535.         ic->img_cb = img_cx->img_cb;
  536.         IMGCBIF_addRef(ic->img_cb, &exc);
  537.  
  538.         if (exc) {
  539.             JMC_DELETE_EXCEPTION(&exc); /* XXXM12N Should really return
  540.                                            exception */
  541.             if (ic->background_color)
  542.                 XP_FREE(ic->background_color);
  543.             XP_FREE(ic->image);
  544.             IL_ReleaseColorSpace(ic->src_header->color_space);
  545.             IL_ReleaseColorSpace(ic->image->header.color_space);
  546.             XP_FREE(ic->src_header);
  547.             XP_FREE(ic);
  548.             return NULL;
  549.         }
  550.     }
  551.     
  552.     il_addtocache(ic);
  553.     ic->is_in_use = TRUE;
  554.     
  555.     return ic;
  556. }
  557.  
  558. void
  559. il_scour_container(il_container *ic)
  560. {
  561.     /* scour quantize, ds, scalerow etc */
  562.     if (ic->image->header.color_space->type == NI_PseudoColor)
  563.         il_free_quantize(ic);
  564.     FREE_IF_NOT_NULL(ic->scalerow);
  565.  
  566.     /* reset a bunch of stuff */
  567.     ic->url    = NULL;
  568.     ic->stream = NULL;
  569.     if (ic->net_cx)
  570.         IL_DestroyDummyNetContext(ic->net_cx);
  571.     ic->net_cx     = NULL;
  572.  
  573.     ic->forced                  = FALSE;
  574.  
  575.     ic->is_alone                = FALSE;
  576. }
  577.  
  578. /*
  579.  * destroy an il_container, freeing it and the image
  580.  */
  581. void
  582. il_delete_container(il_container *ic)
  583. {
  584.     JMCException *exc = NULL;
  585.  
  586.     XP_ASSERT(ic);
  587.     if (ic) {
  588.         ILTRACE(2,("il: delete ic=0x%08x", ic));
  589.  
  590.         /*
  591.          * We can't throw away this container until the netlib
  592.          * stops sending us stuff.  We defer the actual deletion
  593.          * of the container until then.
  594.          */
  595.         if (ic->is_url_loading) {
  596.             ic->state = IC_ABORT_PENDING;
  597.             return;
  598.         }
  599.         
  600.         XP_ASSERT(ic->clients == NULL);
  601.         il_scour_container(ic);
  602.  
  603.         if (ic->background_color){
  604.             XP_FREE(ic->background_color);
  605.         }
  606.  
  607.         if (ic->src_header->transparent_pixel)
  608.             XP_FREE(ic->src_header->transparent_pixel);
  609.         IL_ReleaseColorSpace(ic->src_header->color_space);
  610.         XP_FREE(ic->src_header);
  611.  
  612.         /* delete the image */
  613.         if (!(ic->image || ic->mask))
  614.             return;
  615.         il_destroy_pixmap(ic->img_cb, ic->image);
  616.         if (ic->mask)
  617.             il_destroy_pixmap(ic->img_cb, ic->mask);
  618.         IMGCBIF_release(ic->img_cb, &exc);
  619.         if (exc) {
  620.             JMC_DELETE_EXCEPTION(&exc); /* XXXM12N Should really return
  621.                                            exception */
  622.         }
  623.  
  624.         FREE_IF_NOT_NULL(ic->comment);
  625.         FREE_IF_NOT_NULL(ic->url_address);
  626.         FREE_IF_NOT_NULL(ic->fetch_url);
  627.         
  628.         XP_FREE(ic);
  629.     }
  630. }
  631.  
  632.  
  633. /* Destroy an IL_Pixmap. */
  634. void
  635. il_destroy_pixmap(IMGCBIF *img_cb, IL_Pixmap *pixmap) 
  636. {
  637.     NI_PixmapHeader *header = &pixmap->header;
  638.  
  639.     /* Free the pixmap's bits, as well as the client_data. */
  640.     IMGCBIF_DestroyPixmap(img_cb, NULL, pixmap);
  641.  
  642.     /* Release memory used by the header. */
  643.     IL_ReleaseColorSpace(header->color_space);
  644.     if (header->transparent_pixel)
  645.         XP_FREE(header->transparent_pixel);
  646.  
  647.     /* Release the IL_Pixmap. */
  648.     XP_FREE(pixmap);
  649. }
  650.  
  651.  
  652. static char *
  653. il_visual_info(il_container *ic)
  654. {
  655.     char *msg = (char *)XP_CALLOC(1, 50);
  656.     NI_PixmapHeader *img_header = &ic->image->header;
  657.  
  658.     if (!msg)
  659.         return NULL;
  660.     
  661.     switch (img_header->color_space->type)
  662.         {
  663.         case NI_PseudoColor:
  664.             XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_COLOR),
  665.                        img_header->color_space->pixmap_depth); /* #### i18n */
  666.             break;
  667.  
  668.         case NI_GreyScale:
  669.             if (img_header->color_space->pixmap_depth == 1)
  670.                XP_SPRINTF(msg, XP_GetString(XP_MSG_1BIT_MONO)); /* #### i18n */
  671.             else
  672.                XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_GREYSCALE),
  673.                           img_header->color_space->pixmap_depth);
  674.                                 /* #### i18n */
  675.              break;
  676.  
  677.         case NI_TrueColor:
  678.             XP_SPRINTF(msg, XP_GetString(XP_MSG_XBIT_RGB),
  679.                        img_header->color_space->pixmap_depth); /* #### i18n */
  680.             break;
  681.  
  682.         default:
  683.             XP_ASSERT(0);
  684.             *msg=0;
  685.             break;
  686.         }
  687.  
  688.     return msg;
  689. }
  690.  
  691. /* Define some macros to help us output HTML */
  692. #define CELL_TOP                             \
  693.     StrAllocCat(output,                     \
  694.                 "<TR><TD VALIGN=BASELINE ALIGN=RIGHT><B>");    
  695. #define CELL_TITLE(title)                    \
  696.     StrAllocCat(output, title);
  697. #define CELL_MIDDLE                            \
  698.     StrAllocCat(output,                     \
  699.                 "</B></TD>"                    \
  700.                 "<TD>");
  701. #define CELL_BODY(body)                        \
  702.     StrAllocCat(output, body);
  703. #define CELL_END                            \
  704.     StrAllocCat(output,                     \
  705.                 "</TD></TR>");
  706. #define ADD_CELL(c_title, c_body)            \
  707.     CELL_TOP;                                \
  708.     CELL_TITLE(c_title);                    \
  709.     CELL_MIDDLE;                            \
  710.     CELL_BODY(c_body);                        \
  711.     CELL_END;
  712.  
  713. static char *
  714. il_HTML_image_info(il_container *ic, int long_form, int show_comment)
  715. {
  716.     char tmpbuf[512];           /* Temporary consing area */
  717.     char *output = NULL;
  718.     NI_PixmapHeader *src_header = ic->src_header; /* Source image header. */
  719.     NI_PixmapHeader *img_header = &ic->image->header; /* Destination image
  720.                                                          header. */
  721.     NI_IRGB *img_trans_pixel = img_header->transparent_pixel;
  722.  
  723.     XP_SPRINTF(tmpbuf, "%lu", (long)img_header->widthBytes *
  724.                img_header->height + sizeof(il_container));
  725.     ADD_CELL(XP_GetString(XP_MSG_DECODED_SIZE), tmpbuf); /* #### i18n */
  726.  
  727.     /* #### i18n */
  728. #ifdef XP_WIN16
  729.     XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_WIDTH_HEIGHT), (short)img_header->width,
  730.                (short)img_header->height);
  731. #else
  732.     XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_WIDTH_HEIGHT), img_header->width,
  733.                img_header->height);
  734. #endif
  735.     if ((img_header->width != src_header->width) ||
  736.         (img_header->height != src_header->height))
  737.     {
  738.         /* #### i18n */
  739.         XP_SPRINTF(tmpbuf + strlen(tmpbuf),  XP_GetString(XP_MSG_SCALED_FROM),
  740.                    src_header->width, src_header->height);
  741.     }
  742.     /* #### i18n */
  743.     ADD_CELL(XP_GetString(XP_MSG_IMAGE_DIM), tmpbuf);
  744.  
  745.     if (long_form) {
  746.  
  747.         char *visual_info = il_visual_info(ic);
  748.         if (visual_info) {
  749.             ADD_CELL(XP_GetString(XP_MSG_COLOR), visual_info);
  750.                                                              /* #### i18n */
  751.             XP_FREE(visual_info);
  752.         }
  753.         
  754.         if (img_header->color_space->cmap.map)
  755.             XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_NB_COLORS),
  756.                        img_header->color_space->cmap.num_colors);
  757.                                                              /* #### i18n */
  758.         else
  759.             XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_NONE));   /* #### i18n */
  760.         ADD_CELL(XP_GetString(XP_MSG_COLORMAP), tmpbuf);
  761.                 
  762.         if (img_trans_pixel) {
  763.             if (ic->mask)
  764.                 XP_SPRINTF(tmpbuf,
  765.                            /* #### i18n */
  766.                            XP_GetString(XP_MSG_BCKDRP_VISIBLE));
  767.             else
  768.                 XP_SPRINTF(tmpbuf,
  769.                            /* #### i18n */
  770.                            XP_GetString(XP_MSG_SOLID_BKGND),
  771.                            img_trans_pixel->red,
  772.                            img_trans_pixel->green,
  773.                            img_trans_pixel->blue);
  774.         } else {
  775.             XP_SPRINTF(tmpbuf, XP_GetString(XP_MSG_JUST_NO));    /* #### i18n */
  776.         }
  777.         ADD_CELL(XP_GetString(XP_MSG_TRANSPARENCY), tmpbuf);    /* #### i18n */
  778.     }
  779.  
  780.     if (show_comment && ic->comment) {
  781.         XP_SPRINTF(tmpbuf, "%.500s", ic->comment);
  782.         ADD_CELL(XP_GetString(XP_MSG_COMMENT), tmpbuf);    /* #### i18n */
  783.     }
  784.  
  785.     return output;
  786. }
  787.  
  788. char *
  789. IL_HTMLImageInfo(char *url_address)
  790. {
  791.     il_container *ic;
  792.     char *output = NULL;
  793.     char *il_msg;
  794.     
  795.     for (ic=il_cache.head; ic; ic=ic->next)
  796.     {
  797.         if (!strcmp(ic->url_address, url_address))
  798.             break;
  799.     }
  800.  
  801.     if ((ic == NULL) || (ic->state != IC_COMPLETE))
  802.         return NULL;
  803.  
  804.     il_msg = il_HTML_image_info(ic, TRUE, TRUE);
  805.     if (il_msg == NULL)
  806.         return NULL;
  807.  
  808.     StrAllocCat(output,
  809.                 "<TABLE CELLSPACING=0 CELLPADDING=1 "
  810.                 "BORDER=0 ALIGN=LEFT WIDTH=66%>");
  811.     StrAllocCat(output, il_msg);
  812.     StrAllocCat(output, "</TABLE> <A HREF=\"");
  813.     StrAllocCat(output, url_address);
  814.     StrAllocCat(output, "\"> <IMG WIDTH=90% ALIGN=CENTER SRC=\"");
  815.     StrAllocCat(output, url_address);
  816.     StrAllocCat(output, "\"></A>\n");
  817.  
  818.     XP_FREE(il_msg);
  819.  
  820.     return output;
  821. }
  822.  
  823. /*
  824.  * Create an HTML stream and generate HTML describing
  825.  * the image cache.  Use "about:memory-cache" URL to acess.
  826.  */
  827. int 
  828. IL_DisplayMemCacheInfoAsHTML(FO_Present_Types format_out, IL_URL *urls,
  829.                              OPAQUE_CONTEXT *cx)
  830. {
  831.     char buffer[2048];
  832.     char *output = NULL;
  833.     char *img_info;
  834.     char *address;
  835.     char *escaped;
  836.        NET_StreamClass *stream;
  837.     Bool long_form = FALSE;
  838.     int status;
  839.  
  840.     if(strcasestr(urls->address, "?long"))
  841.         long_form = TRUE;
  842. #ifndef M12N                    /* XXXM12N Cache trace: cleanup/eliminate. */
  843.     else if(strcasestr(urls->address, "?traceon"))
  844.         il_cache_trace = TRUE;
  845.     else if(strcasestr(urls->address, "?traceoff"))
  846.         il_cache_trace = FALSE;
  847. #endif /* M12N */
  848.  
  849.     StrAllocCopy(urls->content_type, TEXT_HTML);
  850.     format_out = CLEAR_CACHE_BIT(format_out);
  851.  
  852.     stream = NET_StreamBuilder(format_out, urls, cx);
  853.     if (!stream)
  854.         return 0;
  855.  
  856.     /*
  857.      * Define a macro to push a string up the stream
  858.      * and handle errors
  859.      */
  860. #define PUT_PART(part)                                                        \
  861. {                                                                           \
  862.     status = (*stream->put_block)(stream,                        \
  863.                                   part ? part : "Unknown",                    \
  864.                                   part ? XP_STRLEN(part) : 7);                \
  865.     if (status < 0)                                                            \
  866.         goto END;                                                           \
  867. }
  868.  
  869.     /* Send something to the screen immediately.  This will force all
  870.      * the images on the page to enter the cache now. Otherwise,
  871.      * layout will be causing cache state to be modified even as we're
  872.      * trying to display it.
  873.      */
  874.     XP_STRCPY(buffer,
  875.               "<TITLE>Information about the Netscape image cache</TITLE>\n");
  876.     PUT_PART(buffer);
  877.                                   
  878.      XP_SPRINTF(buffer, 
  879.                "<h2>Image Cache statistics</h2>\n"
  880.                "<TABLE CELLSPACING=0 CELLPADDING=1>\n"
  881.                "<TR>\n"
  882.                "<TD ALIGN=RIGHT><b>Maximum size:</b></TD>\n"
  883.                "<TD>%ld</TD>\n"
  884.                "</TR>\n"
  885.                "<TR>\n"
  886.                "<TD ALIGN=RIGHT><b>Current size:</b></TD>\n"
  887.                "<TD>%ld</TD>\n"
  888.                "</TR>\n"
  889.                "<TR>\n"
  890.                "<TD ALIGN=RIGHT><b>Number of images in cache:</b></TD>\n"
  891.                "<TD>%d</TD>\n"
  892.                "</TR>\n"
  893.                "<TR>\n"
  894.                "<TD ALIGN=RIGHT><b>Average cached image size:</b></TD>\n"
  895.                "<TD>%ld</TD>\n"
  896.                "</TR>\n"
  897.                "</TABLE>\n"
  898.                "<HR>",
  899.                (long)il_cache.max_bytes,
  900.                (long)il_cache.bytes,
  901.                il_cache.items,
  902.                (il_cache.bytes ?
  903.                 (long)il_cache.bytes / il_cache.items : 0L));
  904.  
  905.     PUT_PART(buffer);
  906.  
  907.     {
  908.         il_container *ic, *l = NULL;
  909.         for (ic=il_cache.head; ic; ic=ic->next) {
  910.             
  911.             /* Sanity check */
  912.             if (l)
  913.                 XP_ASSERT(ic->prev == l);
  914.             l = ic;
  915.  
  916.             /* Don't display uninteresting images */
  917.             if ((ic->state != IC_COMPLETE) &&
  918.                 (ic->state != IC_SIZED)    &&
  919.                 (ic->state != IC_MULTI))
  920.                 continue;
  921.             
  922.             StrAllocCat(output, "<TABLE>\n");
  923.  
  924.             /* Emit DocInfo link to URL */
  925.             address = ic->url_address;
  926.             XP_STRCPY(buffer, "<A TARGET=Internal_URL_Info HREF=about:");
  927.             XP_STRCAT(buffer, address);
  928.             XP_STRCAT(buffer, ">");
  929.             escaped = NET_EscapeHTML(address);
  930.             XP_STRCAT(buffer, escaped);
  931.             XP_FREE(escaped);
  932.             XP_STRCAT(buffer, "</A>");
  933.             ADD_CELL("URL:", buffer);
  934.             
  935.             /* Rest of image info (size, colors, depth, etc.) */
  936.             img_info = il_HTML_image_info(ic, long_form, FALSE);
  937.             if (img_info) {
  938.                 StrAllocCat(output, img_info);
  939.                 XP_FREE(img_info);
  940.             }
  941.             
  942.             StrAllocCat(output, "</TABLE><P>\n");
  943.         }
  944.  
  945.         if (output)
  946.             PUT_PART(output);
  947.     }
  948.         
  949.   END:
  950.      
  951.     if (output)
  952.         XP_FREE(output);
  953.      
  954.     if(status < 0)
  955.         (*stream->abort)(stream, status);
  956.     else
  957.         (*stream->complete)(stream);
  958.  
  959.     return 1;
  960. }
  961.  
  962. il_container *
  963. il_removefromcache(il_container *ic)
  964. {
  965.     int32 image_bytes;
  966.     NI_PixmapHeader *img_header = &ic->image->header;
  967.  
  968.     XP_ASSERT(ic); 
  969.     if (ic)
  970.     {
  971.         ILTRACE(2,("il: remove ic=0x%08x from cache\n", ic));
  972.         XP_ASSERT(ic->next || ic->prev || (il_cache.head == il_cache.tail));
  973.  
  974.         /* Remove entry from doubly-linked list. */
  975.         if (il_cache.head == ic)
  976.             il_cache.head = ic->next;
  977.  
  978.         if (il_cache.tail == ic)
  979.             il_cache.tail = ic->prev;
  980.  
  981.         if (ic->next)
  982.             ic->next->prev = ic->prev;
  983.         if (ic->prev)
  984.             ic->prev->next = ic->next;
  985.  
  986.         ic->next = ic->prev = NULL;
  987.  
  988.         image_bytes = ((int32) img_header->widthBytes) * img_header->height;
  989.  
  990. /* this assert gets triggered all the time. Take it out or fix it */
  991. /*        XP_ASSERT (il_cache.bytes >= (int32)image_bytes); */
  992.  
  993.         if (il_cache.bytes <  (int32)image_bytes)
  994.             il_cache.bytes = 0;
  995.         else        
  996.             il_cache.bytes -= image_bytes;
  997.         il_cache.items--;
  998.         XP_ASSERT(il_cache.items >= 0);
  999.     }
  1000.     return ic;
  1001. }
  1002.  
  1003. void
  1004. il_adjust_cache_fullness(int32 bytes)
  1005. {
  1006.     il_cache.bytes += bytes;
  1007.     XP_ASSERT(il_cache.bytes >= 0);
  1008.  
  1009.     /* Safety net - This should never happen. */
  1010.     if (il_cache.bytes < 0)
  1011.         il_cache.bytes = 0;
  1012.  
  1013.     /* We get all of the memory cache.  (Only images are cached in memory.) */
  1014.     il_reduce_image_cache_size_to(image_cache_size);
  1015.  
  1016.     /* Collect some debugging info. */
  1017.     if (il_cache.bytes > il_cache.max_bytes)
  1018.         il_cache.max_bytes = il_cache.bytes;
  1019. }
  1020.  
  1021. static void
  1022. il_addtocache(il_container *ic)
  1023. {
  1024.     NI_PixmapHeader *img_header = &ic->image->header;
  1025.  
  1026.     ILTRACE(2,("il:    add ic=0x%08x to cache\n", ic));
  1027.     XP_ASSERT(!ic->prev && !ic->next);
  1028.  
  1029.     /* Thread onto doubly-linked list, kept in LRU order */
  1030.     ic->prev = NULL;
  1031.     ic->next = il_cache.head;
  1032.     if (il_cache.head)
  1033.         il_cache.head->prev = ic;
  1034.     else {
  1035.         XP_ASSERT(il_cache.tail == NULL);
  1036.         il_cache.tail = ic;
  1037.     }
  1038.     il_cache.head = ic;
  1039.  
  1040.     /* Image storage is added in when image is sized */
  1041.     if (ic->sized)
  1042.         il_cache.bytes += (uint32)img_header->widthBytes * img_header->height;
  1043.     il_cache.items++;
  1044. }
  1045.  
  1046.  
  1047. /* Set limit on approximate size, in bytes, of all pixmap storage used
  1048.    by the imagelib.  */
  1049. void
  1050. IL_SetCacheSize(uint32 new_size)
  1051. {
  1052.     image_cache_size = new_size;
  1053.     il_reduce_image_cache_size_to(new_size);
  1054. }
  1055.  
  1056.  
  1057. void
  1058. il_reduce_image_cache_size_to(uint32 new_size)
  1059. {
  1060.     int32 last_size = 0;
  1061.     
  1062.     while ((il_cache.bytes > (int32)new_size) && (il_cache.bytes != last_size)) {
  1063.         last_size = il_cache.bytes;
  1064.         IL_ShrinkCache();
  1065.     }
  1066. }
  1067.  
  1068. uint32
  1069. IL_ShrinkCache(void)
  1070. {
  1071.     il_container *ic;
  1072.     ILTRACE(3,("shrink"));
  1073.  
  1074.     for (ic = il_cache.tail; ic; ic = ic->prev)
  1075.     {
  1076.         if (ic->is_in_use)
  1077.             continue;
  1078.         
  1079. #ifndef M12N                    /* XXXM12N Cache trace: cleanup/eliminate. */
  1080.         if (il_cache_trace) {
  1081.             char buffer[1024];
  1082.             MWContext *alert_context =
  1083.                 XP_FindContextOfType(NULL, MWContextBrowser);
  1084.             if(!alert_context)
  1085.                 alert_context = XP_FindContextOfType(NULL, MWContextPane);
  1086.             
  1087.             sprintf(buffer,
  1088.                     XP_GetString(XP_MSG_COMPRESS_REMOVE), ic->url_address);
  1089.  
  1090.             if (alert_context)
  1091.                 FE_Alert(alert_context, buffer);
  1092.         }
  1093. #endif /* M12N */
  1094.         il_removefromcache(ic);
  1095.         il_delete_container(ic);
  1096.         break;
  1097.     }
  1098.  
  1099.     return il_cache.bytes;
  1100. }
  1101.  
  1102. uint32 IL_GetCacheSize()
  1103. {    
  1104.     return il_cache.bytes;
  1105. }
  1106.  
  1107.  
  1108. /* Attempt to release the memory used by a specific image in the image
  1109.    cache.  The memory won't be released if the image is still in use
  1110.    by one or more clients.  XXX - Can we get rid of this call ?  Why
  1111.    the hell do we need this ? */
  1112. void
  1113. IL_UnCache(IL_Pixmap *pixmap)
  1114. {
  1115.     il_container *ic;
  1116.     if (pixmap)
  1117.     {
  1118.         for (ic=il_cache.head; ic; ic=ic->next)
  1119.         {
  1120.             /* Check the pixmap argument against both the image and mask
  1121.                pixmaps of the container. */
  1122.             if (((ic->image == pixmap) || (ic->mask == pixmap)) &&
  1123.                 !ic->is_in_use)
  1124.             {
  1125.                 il_removefromcache(ic);
  1126.                 il_delete_container(ic);
  1127.                 return;
  1128.             }
  1129.         }
  1130.     }
  1131. }
  1132.  
  1133.  
  1134. /* Free num_bytes of memory by flushing the Least Recently Used (LRU) images
  1135.    from the image cache. */
  1136. void
  1137. IL_FreeMemory(IL_GroupContext *image_context, uint32 num_bytes)
  1138. {
  1139.     /* XXXM12N Implement me. */
  1140. }
  1141.  
  1142. PRBool
  1143. il_add_client(IL_GroupContext *img_cx, il_container *ic,
  1144.               IL_ImageReq* image_req, int is_view_image)
  1145. {
  1146.     PRBool added_context;
  1147.  
  1148.     ILTRACE(3, ("il: add_client ic=0x%08x, image_req =0x%08x\n", ic,
  1149.                 image_req));
  1150.  
  1151.     image_req->is_view_image = is_view_image;
  1152.  
  1153.     /* Add the client to the end of the container's client list. */
  1154.     if (!ic->lclient)
  1155.         ic->clients = image_req;
  1156.     else
  1157.         ic->lclient->next = image_req;
  1158.     ic->lclient = image_req;
  1159.  
  1160.     /* Now add the client context to the container's client context list,
  1161.        (if necessary,) and also add the container to the context's list of
  1162.        containers.  Note: a FALSE return value could mean that the context
  1163.        did not need to be added. */
  1164.     added_context = il_add_client_context(img_cx, ic);
  1165.     
  1166.     /* Always return TRUE. */
  1167.     return PR_TRUE;
  1168. }
  1169.  
  1170. /* Delete an IL_ImageReq from the list of clients for an image container.
  1171.    Return TRUE if successful, FALSE otherwise. */
  1172. static PRBool
  1173. il_delete_client(il_container *ic, IL_ImageReq *image_req)
  1174. {
  1175.     IL_GroupContext *img_cx;
  1176.     IL_NetContext *net_cx;
  1177.     IL_ImageReq *current_req;
  1178.     IL_ImageReq *prev_req = NULL;
  1179.  
  1180.     /* Search for the given image request in the container's client list. */
  1181.     current_req = ic->clients;
  1182.     while (current_req) {
  1183.         if (current_req == image_req)
  1184.             break;
  1185.         prev_req = current_req;
  1186.         current_req = current_req->next;
  1187.     }
  1188.  
  1189.     /* If the image request wasn't found in the client list, return FALSE. */
  1190.     if (!current_req)
  1191.         return PR_FALSE;
  1192.  
  1193.     /* Image request was found in the client list i.e. current_req is the
  1194.        same as image_req. */
  1195.     if (current_req == ic->clients)
  1196.         ic->clients = current_req->next;
  1197.     else
  1198.         prev_req->next = current_req->next;
  1199.  
  1200.     if (current_req == ic->lclient)
  1201.         ic->lclient = prev_req;
  1202.     
  1203.     img_cx = current_req->img_cx;
  1204.     net_cx = current_req->net_cx;   /* This is destroyed below. */
  1205.     XP_FREE(current_req);           /* Delete the image request. */
  1206.  
  1207.     /* Decrement the number of unique images for the given image request's
  1208.        image context, but be careful to do this only when there are no other
  1209.        clients of this image with the same context. */
  1210.     current_req = ic->clients;
  1211.     while (current_req) {
  1212.         if (current_req->img_cx == img_cx)
  1213.             break;
  1214.         current_req = current_req->next;
  1215.     }
  1216.     if (!current_req) {         /* The given image request's context had no
  1217.                                    other requests associated with the image. */
  1218.         PRBool removed_context;
  1219.         
  1220.         /* Remove the context from the container's list of client contexts.
  1221.            Also remove the container from the context's list of containers. */
  1222.         removed_context = il_remove_client_context(img_cx, ic);
  1223.  
  1224.          if (ic->clients) {
  1225.             /* If the deleted image request's image context happened to be the
  1226.                image context used by the container, then we (arbitrarily) set the
  1227.                container's image context to be that of the first client in the
  1228.                list.  We only have to do this if the client list isn't empty. */ 
  1229.              if (ic->img_cx == img_cx)
  1230.                  ic->img_cx = ic->clients->img_cx;
  1231.             /* The container's net context may be about to become invalid, so
  1232.                give the container a different one which is known to be valid. */
  1233.              if (ic->net_cx && !XP_MEMCMP(ic->net_cx, net_cx, sizeof(IL_NetContext))) {
  1234.                  IL_DestroyDummyNetContext(ic->net_cx);
  1235.                  ic->net_cx = IL_CloneDummyNetContext(ic->clients->net_cx);
  1236.              }
  1237.         } 
  1238.     }
  1239.  
  1240.     /* Destroy the net context for the client we just destroyed. */
  1241.     IL_DestroyDummyNetContext(net_cx);
  1242.  
  1243.     ILTRACE(3, ("il: delete_client ic=0x%08x, image_req =0x%08x\n", ic,
  1244.                 image_req));
  1245.  
  1246.     return PR_TRUE;
  1247. }
  1248.  
  1249. static void
  1250. il_delete_all_clients(il_container *ic)
  1251. {
  1252.     IL_ImageReq *image_req;
  1253.     IL_ImageReq *next_req = NULL;
  1254.  
  1255.     for (image_req = ic->clients; image_req; image_req = next_req) {
  1256.         next_req = image_req->next;
  1257.         il_delete_client(ic, image_req);
  1258.     }
  1259. }
  1260.  
  1261.  
  1262. /* One-time image library initialization.
  1263.    = Initialize internal state
  1264.    = Scan image plug-in directory
  1265.    = Register individual image decoders with the netlib */
  1266. int
  1267. IL_Init()
  1268. {
  1269.     /* XXXM12N - finish me. */
  1270.     return TRUE;
  1271. }
  1272.  
  1273.  
  1274. /* Used when exiting the client, this code frees all imagelib data structures.
  1275.    This is done for two reasons:
  1276.      1) It makes leakage analysis of the heap easier, and
  1277.      2) It causes resources to be freed on Windows that would otherwise persist
  1278.         beyond the browser's lifetime.
  1279.  
  1280.    XXX - Actually, right now it just frees the cached images.  There are still
  1281.          a couple of other places that leaks need to be fixed. */
  1282. void
  1283. IL_Shutdown()
  1284. {
  1285.     il_container *ic, *ic_next;
  1286.     for (ic = il_cache.head; ic; ic = ic_next)
  1287.     {
  1288.         ic_next = ic->next;
  1289.         il_delete_all_clients(ic);
  1290.         il_removefromcache(ic);
  1291.         il_delete_container(ic);
  1292.     }
  1293.  
  1294.     XP_ASSERT(il_cache.bytes == 0);
  1295. }
  1296.  
  1297. /* Release a reference to an image lib request.  If there are no other
  1298.    clients of the request's associated pixmap, any related netlib
  1299.    activity is terminated and pixmap storage may be reclaimed.  Also,
  1300.    if the last client is removed, the container can be reclaimed,
  1301.    either by storing it in the cache or throwing it away.
  1302. */
  1303. void
  1304. IL_DestroyImage(IL_ImageReq *image_req)
  1305. {
  1306.     IL_GroupContext *img_cx;
  1307.     PRBool client_deleted;
  1308.     il_container *ic, *ic2, *ic2_next;
  1309.     il_container_list *ic_list;
  1310.  
  1311.     /* Check for a NULL request. */
  1312.     if (!image_req)
  1313.         return;
  1314.  
  1315.     /* Notify observers that the image request is being destroyed.  This
  1316.        provides an opportunity for observers to remove themselves from the
  1317.        observer callback list and to do any related cleanup. */
  1318.     il_image_destroyed_notify(image_req);
  1319.     
  1320.     /* Get the container and context for this image request. */
  1321.     ic = image_req->ic;
  1322.     img_cx = image_req->img_cx;
  1323.  
  1324.     /* Certain requests, such as requests for internal image icons, do not
  1325.        have an image container.  For these cases, there is nothing else to
  1326.        be done, so return early.
  1327.        XXXM12N This will not be necessary once icons are completely separated
  1328.        from the Image Library. */
  1329.     if (!ic){
  1330.     /* editing icons don't have ic's but need to be freed */    
  1331.     if( image_req->net_cx )
  1332.         IL_DestroyDummyNetContext( image_req->net_cx );
  1333.     XP_FREE( image_req );
  1334.         return;
  1335.     }
  1336.  
  1337.     ic_list = img_cx->container_list;
  1338.     ILTRACE(1, ("il: IL_DestoyImage: ic = 0x%08x\n", ic));
  1339.     XP_ASSERT(ic_list);
  1340.     if (!ic_list)
  1341.         return;
  1342.  
  1343.     /* Delete the image request from the container's list of clients. */
  1344.     client_deleted = il_delete_client(ic, image_req);
  1345.     XP_ASSERT(client_deleted);
  1346.     
  1347.     /* The image container can't be deleted until all clients are done. */
  1348.     if (ic->clients)
  1349.         return;
  1350.  
  1351.     /* Don't allow any new streams to be created in
  1352.        il_image_complete() as a result of a looping animation.  (It's
  1353.        possible that this image is being freed before the stream
  1354.        completes/aborts.  This can happen in autoscroll mode and the
  1355.        editor apparently allows this to occur also.) */
  1356.     ic->loop_count = 0;
  1357.  
  1358.     il_image_abort(ic);
  1359.     
  1360.     XP_ASSERT(img_cx->num_containers >= 0);
  1361.     if (!img_cx->num_containers) {
  1362.         XP_ASSERT(!img_cx->container_list);
  1363.         img_cx->container_list = 0;
  1364.         img_cx->num_loading = 0;
  1365.         img_cx->num_aborted = 0;
  1366.     }
  1367.  
  1368.     if ((ic->state != IC_COMPLETE) || ic->multi ||
  1369.         ic->rendered_with_custom_palette ||
  1370.         (NET_URL_Type(ic->url_address) == MOCHA_TYPE_URL)
  1371.         ) {
  1372.         il_removefromcache(ic);
  1373.         il_delete_container(ic);
  1374.         return;
  1375.     }
  1376.  
  1377.     /* Don't allow duplicate entries in cache */
  1378.     for (ic2 = il_cache.head; ic2; ic2 = ic2_next) {
  1379.         ic2_next = ic2->next;
  1380.         if ((!ic2->is_in_use) && il_images_match(ic, ic2)) {
  1381.             il_removefromcache(ic2);
  1382.             il_delete_container(ic2);
  1383.         }
  1384.     }
  1385.  
  1386.     ic->is_in_use = FALSE;
  1387. }
  1388.  
  1389.  
  1390. /* This routine is a "safety net", in case layout didn't free up all the
  1391.  * images on a page.  It assumes layout made a mistake and frees them anyway.
  1392.  */
  1393. void
  1394. IL_DestroyImageGroup(IL_GroupContext *img_cx)
  1395. {
  1396.     IL_ImageReq *image_req, *next_req;
  1397.     il_container *ic;
  1398.     il_container_list *ic_list, *ic_list_next;
  1399.     
  1400.     if (img_cx == NULL)
  1401.         return;
  1402.     
  1403.     if (img_cx->num_containers > 0) {
  1404.         XP_ASSERT(img_cx->container_list);
  1405.  
  1406.         for (ic_list = img_cx->container_list; ic_list;
  1407.              ic_list = ic_list_next) {
  1408.             ic_list_next = ic_list->next;
  1409.             ic = ic_list->ic;            
  1410.             ic->forced = 0;
  1411.  
  1412.             for (image_req = ic->clients; image_req; image_req = next_req) {
  1413.                 next_req = image_req->next;
  1414.                 if (image_req->img_cx == img_cx)
  1415.                     IL_DestroyImage(image_req);
  1416.             }
  1417.         }
  1418.         XP_ASSERT(img_cx->num_containers == 0);
  1419.         XP_ASSERT(img_cx->container_list == NULL);
  1420.     }
  1421.   
  1422.     ILTRACE(1, ("il: IL_DestroyImageGroup\n"));
  1423. }
  1424.