diff --git a/Resources/UK/Phoenix/Choices b/Resources/UK/Phoenix/Choices
index 8e841033efcabfcd10c0eb18eb7cb0ace435cd63..09b804ec9dfc69263a41bd316266772ef2f8b289 100644
--- a/Resources/UK/Phoenix/Choices
+++ b/Resources/UK/Phoenix/Choices
@@ -224,6 +224,35 @@ PlugInControl:asap
 ClientPull:yes
 SeeFetches:no
 
+# Image garbage collection
+# ========================
+#
+# CollectAfter     - If 'yes', garbage collection occurs after a new page
+#                    fetch is completed (free memory etc. permitting). This may
+#                    mean higher transient memory requirements of course. If
+#                    'no', garbage collection occurs the moment a page is
+#                    cleared ready for a new one to fetch. This will affect how
+#                    high you might want to set UnusedImageLimit.
+# FreeRAMLimit     - If free memory falls below this amount when a new image is
+#                    to be created, the garbage collection system is run to try
+#                    and get more RAM. The amount is specified in kilobytes.
+#                    Only as many images as need to be deleted to maintain the
+#                    required free memory, are deleted. So, use 0 for unlimited
+#                    (UnusedImageLimit becomes the only constraint).
+# UnusedImageLimit - If the number of images a browser is holding ready for
+#                    garbage collection exceeds the value given here, get rid
+#                    of an unused image - so this number of unused images will
+#                    be reached and thereafter maintained, since for each image
+#                    created, one pending garbage collection is removed. Use
+#                    zero to effectively disable image garbage collection;
+#                    images from a current page are discard the moment that
+#                    browser window goes to a new location. Use -1 for
+#                    unlimited (FreeRAMLimit becomes the only constraint).
+
+CollectAfter:yes
+FreeRAMLimit:2097512
+UnusedImageLimit:8
+
 # Hotlist controls
 # ================
 #
diff --git a/c/Fetch b/c/Fetch
index 8e42e9360fa3fc4b27afac40b06f410969fe8546..01bf826062f9d5ac3114d5652eee7b8075cf55f8 100644
--- a/c/Fetch
+++ b/c/Fetch
@@ -1266,12 +1266,15 @@ void fetch_preprocess_token(browser_data * b, HStream * tptr)
 
         /* Try adding this title to the history, ignoring any errors */
 
-        history_add_title(browser_fetch_url(b), p);
+        history_add_title(browser_current_url(b), p);
       }
     }
 
     if (tptr->tag == BODY)
     {
+      int bg_set = 0;
+      int fg_set = 0;
+
       /* The BODY tag. All sorts of exciting stuff in here... */
 
       if (HtmlBODYbgcolour(tptr) != NULL_COLOUR)
@@ -1292,6 +1295,8 @@ void fetch_preprocess_token(browser_data * b, HStream * tptr)
         if (b->background_image < 0) b->antialias_colour = b->background_colour;
 
         browser_update_bottom(b, 0);
+
+        bg_set = 1;
       }
 
       /* Background images */
@@ -1302,19 +1307,31 @@ void fetch_preprocess_token(browser_data * b, HStream * tptr)
         /* and remember the image number in the browser_data structure  */
 
         image_new_image(b, HtmlBODYbackground(tptr), tptr, 2, NULL);
+
+        b->antialias_colour = redraw_backcol(b); /* For the time being, at least */
+
+        bg_set = 1;
+      }
+      else
+      {
+        b->background_image = -1;
+        b->antialias_colour = redraw_backcol(b);
       }
 
       /* Get the rest of the colour info out */
 
-      if (HtmlBODYtext (tptr) != NULL_COLOUR) b->text_colour     = HtmlBODYtext (tptr);
-      if (HtmlBODYlink (tptr) != NULL_COLOUR) b->link_colour     = HtmlBODYlink (tptr);
-      if (HtmlBODYvlink(tptr) != NULL_COLOUR) b->used_colour     = HtmlBODYvlink(tptr);
-      if (HtmlBODYalink(tptr) != NULL_COLOUR) b->followed_colour = HtmlBODYalink(tptr);
+      if (HtmlBODYtext (tptr) != NULL_COLOUR) fg_set = 1, b->text_colour     = HtmlBODYtext (tptr);
+      if (HtmlBODYlink (tptr) != NULL_COLOUR) fg_set = 1, b->link_colour     = HtmlBODYlink (tptr);
+      if (HtmlBODYvlink(tptr) != NULL_COLOUR) fg_set = 1, b->used_colour     = HtmlBODYvlink(tptr);
+      if (HtmlBODYalink(tptr) != NULL_COLOUR) fg_set = 1, b->followed_colour = HtmlBODYalink(tptr);
 
       /* Pull out the onload and onunload scripts */
 
-      if (HtmlBODYonload  (tptr)) b->onload   = HtmlBODYonload  (tptr);
+      if (HtmlBODYonload(tptr))   b->onload   = HtmlBODYonload(tptr);
+      else                        b->onload   = bg_set ? "" : NULL;
+
       if (HtmlBODYonunload(tptr)) b->onunload = HtmlBODYonunload(tptr);
+      else                        b->onunload = bg_set ? "" : NULL;
     }
 
     /* Deal with META... tags */
@@ -1700,8 +1717,9 @@ void fetch_fetcher(browser_data * b)
         /* Write to the global history */
 
         if (
-             b->displayed == Display_Fetched_Page ||
-             b->displayed == Display_External_Image
+             b->displayed == Display_Fetched_Page   ||
+             b->displayed == Display_External_Image ||
+             b->displayed == Display_About_Page
            )
            history_record(b, b->urlddata);
 
@@ -1747,35 +1765,58 @@ void fetch_fetcher(browser_data * b)
         b->final_token       = NULL;                     /* Last HStream structure dealt with     */
         b->last_char         = ' ';                      /* Last character dealt with             */
 
-        b->background_colour = -1;                       /* Background colour, or -1 for default  */
-
-        b->background_image  = -1;                       /* Image no. of background image, 0=none */
         b->text_colour       = choices.text_colour;      /* Body text default colour              */
         b->link_colour       = choices.link_colour;      /* Link text default colour              */
         b->used_colour       = choices.used_colour;      /* Followed link default colour          */
-        b->antialias_colour  = redraw_backcol(b);        /* Colour to anti-alias to, or -1=none   */
         b->followed_colour   = choices.followed_colour;  /* Following link default colour         */
         b->selected_colour   = choices.selected_colour;  /* Selected (highlighted) link colour    */
 
-        b->onload            = NULL;                     /* <BODY onload> attribute               */
-        b->onunload          = NULL;                     /* <BODY onunload> attribute             */
+        if (b->reloading || (b->onload == NULL && b->onunload == NULL))
+        {
+          b->background_colour = -1;                     /* Background colour, or -1 for default  */
+          b->background_image  = -1;                     /* Image no. of background image, 0=none */
+          b->antialias_colour  = redraw_backcol(b);      /* Colour to anti-alias to, or -1=none   */
+        }
+        else
+        {
+          b->onload            = NULL;                   /* <BODY onload> attribute               */
+          b->onunload          = NULL;                   /* <BODY onunload> attribute             */
+        }
+
+// Eeek! Needs doing, but not here. The SCRIPT callbacks would have gone off
+// by now, so all of the functions just defined for this page are trashed;
+// and trashed *after* the scripts have already run with the previous page's
+// stuff visible. Fix me, fix me, fix me...
+//
+//        /* For JavaScript, close down the old context for the window and */
+//        /* get a new one - we shouldn't be able to access things defined */
+//        /* in the previous page in this new page.                        */
+//
+//        #ifdef JAVASCRIPT
+//
+//          javascript_lose_context(b);
+//
+//          if (fetch_chkerror(b, javascript_gain_context(b, b->ancestor))) return;
+//
+//        #endif
 
         /* If this was a History-based fetch, clear the flag */
 
-        b->from_history      = 0;
+        b->from_history     = 0;
 
         /* Ensure the nesting level and filling frame counters are reset */
 
-        b->nesting_level     = 0;
-        b->filling_frame     = 0;
+        b->nesting_level    = 0;
+        b->filling_frame    = 0;
 
         /* Allow scroll bars to be reset */
 
-        b->tools_lock        = 1;
+        b->tools_lock       = 1;
 
         /* Cancel any pending automatic fetches */
 
         if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b);
+
         b->meta_refresh_at  = 0;
         b->meta_refresh_url = NULL;
 
@@ -1829,6 +1870,10 @@ void fetch_fetcher(browser_data * b)
 
         image_discard(b);
 
+        /* If forcing a reload, garbage collect immediately */
+
+        if (b->reloading) image_gc(b, 0, 1);
+
         /* Flag that images need to be garbage collected later (as */
         /* free memory drops down after the fetch), and call the   */
         /* garbage collector now since we may have exceeded the    */
@@ -1890,18 +1935,6 @@ void fetch_fetcher(browser_data * b)
         toolbars_cancel_status(b, Toolbars_Status_Connecting);
         toolbars_update_status(b, Toolbars_Status_Fetching);
 
-//        /* For JavaScript, close down the old context for the window and */
-//        /* get a new one - we shouldn't be able to access things defined */
-//        /* in the previous page in this new page.                        */
-//
-//        #ifdef JAVASCRIPT
-//
-//          javascript_lose_context(b);
-//
-//          if (fetch_chkerror(b, javascript_gain_context(b, b->ancestor))) return;
-//
-//        #endif
-
         /* Since the new fetch is now official, update the current and previous */
         /* page variables                                                       */
 
@@ -2082,6 +2115,10 @@ _kernel_oserror * fetch_cancel(browser_data * b)
   if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b);
   b->meta_refresh_at = 0;
 
+  /* If an onLoad JavaScript specifier was present, cancel it too */
+
+  if (b->onload) b->onload = "";
+
   /* If not fetching, exit here */
 
   if (!fetch_fetching(b)) return NULL;
diff --git a/c/Frames b/c/Frames
index 67b27d69e35ae72de40081f168f063f1da6f4bf5..dd1601cb8169bd438b4e15d55adfa72e878c9d88 100644
--- a/c/Frames
+++ b/c/Frames
@@ -2568,7 +2568,6 @@ browser_data * frames_find_target(browser_data * b, const char * target)
 /* the relevant browser_data structure with      */
 /* frames_find_browser_from_index.               */
 /*                                               */
-/*                                               */
 /* Parameters: Pointer to a browser_data struct  */
 /*             representing the parent (it and   */
 /*             all its children are searched for */
diff --git a/c/Images b/c/Images
index 2eb21b178f3074b2a62f1ef93fa0c784b55309f5..0f3ed07c1390488f4e3a61bf73f363d2976919e7 100644
--- a/c/Images
+++ b/c/Images
@@ -84,10 +84,15 @@ static void              image_remove_all_data        (int image);
 static void              image_delay                  (browser_data * b, int image);
 static void              image_abandon                (browser_data * b, int image);
 static void              image_prepare                (browser_data * b, HStream * token, int image, int background);
-static void              image_delete_image_entry     (int image);
+
+static void              image_mark_as_deleted        (int i);
+static void              image_mark_as_gcable         (int i);
 static void              image_destroy                (int image);
+static int               image_renumber               (int old, int new);
+static int               image_purge_deleted_entries  (void);
 static void              image_post_deletion_clearup  (void);
 static int               image_gc_oldest              (browser_data * b);
+
 static void              image_refetch                (browser_data * b, int image, int priority, int redraw);
 
 static _kernel_oserror * image_update_area            (browser_data * b, int x, int y, BBox * ubox, int image, int redraw);
@@ -95,7 +100,7 @@ static _kernel_oserror * image_update_image           (browser_data * b, int ima
 
 static _kernel_oserror * image_register_filler        (int xref);
 
-static int               image_data_offset            (int image);
+//static int               image_data_offset            (int image);
 static int               image_count_fetches          (browser_data * b);
 
 static int               image_get_token_image_xref   (browser_data * b, HStream * token);
@@ -111,7 +116,6 @@ static int               image_can_be_saved_as_sprite (browser_data * b, int i);
 /* Locals */
 
 static image_info   * idata       = NULL; /* Flex block for image_info structures (referenced as an array) */
-static char         * ddata       = NULL; /* Flex block for in-transit fetch data, URLs, etc.              */
 static int            nimages     = 0;    /* Current number of image_info structures in idata array        */
 static int            lastimage   = 0;    /* Used to work out what image_process_null should handle next   */
 static int            animhandler = 0;    /* Flag so code knows when to (de)register GIF animation handler */
@@ -426,47 +430,21 @@ static _kernel_oserror * image_load_chunk(browser_data * b, int image, char * bu
 
 static void image_remove_data(browser_data * b, int image)
 {
-  int p, oldsize, remove;
-
   #ifdef TRACE
     if (tl & (1u<<15)) Printf("image_remove_data: Called for image %d\n",image);
   #endif
 
-  /* Get the current data size */
-
-  oldsize = flex_size((flex_ptr) &ddata);
-
-  /* The alloc field holds the total amount of data associated */
-  /* with the image, and the ualloc field holds the amount     */
-  /* that the URL uses. So subtract ualloc from alloc to get   */
-  /* the amount that must be removed.                          */
-
-  remove = idata[image].alloc - idata[image].ualloc;
-
-  if (remove)
+  if (idata[image].flex->fetchdata)
   {
-    /* Need to shuffle the rest of the flex block */
-    /* down and shrink the block, if this isn't   */
-    /* the last image (i.e. p != oldsize).        */
-
-    p = image_data_offset(image) + idata[image].alloc;
-
-    if (p != oldsize) memmove(ddata + p - remove, ddata + p, oldsize - p);
-
-    flex_extend((flex_ptr) &ddata, oldsize - remove);
-
     #ifdef TRACE
-      flexcount -= remove;
+      flexcount -= flex_size(&idata[image].flex->fetchdata);
       if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
     #endif
-  }
 
-  /* Subtract the amount removed from the alloc field */
-  /* and set isize to zero, i.e. we're not storing    */
-  /* any image data anymore.                          */
+    flex_free(&idata[image].flex->fetchdata);
 
-  idata[image].alloc -= remove;
-  idata[image].isize  = 0;
+    idata[image].flex->fetchdata = NULL;
+  }
 
   #ifdef TRACE
     if (tl & (1u<<15)) Printf("image_remove_data: Successful\n");
@@ -485,45 +463,38 @@ static void image_remove_data(browser_data * b, int image)
 
 static void image_remove_all_data(int image)
 {
-  int p, oldsize, remove;
-
   #ifdef TRACE
     if (tl & (1u<<15)) Printf("image_remove_all_data: Called for image %d\n",image);
   #endif
 
-  /* Get the current data size */
-
-  oldsize = flex_size((flex_ptr) &ddata);
+  if (!idata[image].flex) return;
 
-  /* The alloc field holds the total amount of data associated */
-  /* with the image.                                           */
-
-  remove = idata[image].alloc;
-
-  if (remove)
+  if (idata[image].flex->urldata)
   {
-    /* Need to shuffle the rest of the flex block */
-    /* down and shrink the block, if this isn't   */
-    /* the last image                             */
-
-    p = image_data_offset(image);
+    #ifdef TRACE
+      flexcount -= flex_size((flex_ptr) &idata[image].flex->urldata);
+      if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
+    #endif
 
-    if (oldsize - p - remove) memmove(ddata + p, ddata + p + remove, oldsize - p - remove);
+    flex_free((flex_ptr) &idata[image].flex->urldata);
 
-    flex_extend((flex_ptr) &ddata, oldsize - remove);
+    idata[image].flex->urldata = NULL;
+  }
 
+  if (idata[image].flex->fetchdata)
+  {
     #ifdef TRACE
-      flexcount -= remove;
+      flexcount -= flex_size(&idata[image].flex->fetchdata);
       if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
     #endif
-  }
 
-  /* Subtract the amount removed from the alloc field */
-  /* and set isize to zero, i.e. we're not storing    */
-  /* any image data anymore.                          */
+    flex_free(&idata[image].flex->fetchdata);
+
+    idata[image].flex->fetchdata = NULL;
+  }
 
-  idata[image].alloc = 0;
-  idata[image].isize = 0;
+  free(idata[image].flex);
+  idata[image].flex = NULL;
 
   #ifdef TRACE
     if (tl & (1u<<15)) Printf("image_remove_all_data: Successful\n");
@@ -864,7 +835,7 @@ static void image_prepare(browser_data * b, HStream * token, int image, int back
 
 _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream * token, int background, int * handle)
 {
-  int size, ok, ulen;
+  int ok, ulen;
   int xref;
   int use_xref   = 0;
   int tried_once = 0;
@@ -931,15 +902,15 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
         /* Can we simply create a cross reference to another image? */
 
         if (
-             idata[i].alloc                                            &&  /* The image has data allocated for it                 */
-             idata[i].xref < 0                                         &&  /* The image doesn't cross reference things itself     */
-             idata[i].background == !!background                       &&  /* We're not trying to make a b/g an f/g or vice versa */
-             !strcmp(url, ddata + image_data_offset(i))                &&  /* This holds a URL which matches that passed in       */
+             idata[i].flex->urldata                                  &&  /* The image has an associated URL                     */
+             idata[i].xref < 0                                       &&  /* The image doesn't cross reference things itself     */
+             idata[i].background == !!background                     &&  /* We're not trying to make a b/g an f/g or vice versa */
+             !strcmp(url, idata[i].flex->urldata)                    &&  /* This holds a URL which matches that passed in       */
              (
-               !background ||                                              /* This isn't a background image, or...                */
+               !background ||                                            /* This isn't a background image, or...                */
                (
-                 background &&                                             /* If this is a background image, the background       */
-                 idata[i].owner->background_colour == b->background_colour /* colours match in the two owner browsers             */
+                 background &&                                           /* If this is a background image, the background       */
+                 redraw_backcol(idata[i].owner) == redraw_backcol(b)     /* colours match in the two owner browsers             */
                )
              )
            )
@@ -951,14 +922,10 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
     }
   }
 
-  if (xref >= 0) ulen = 0; /* Don't need any extra data for a cross referenced image. */
-  else
-  {
-    ulen = (strlen(url) + 1);
-    ulen = (int) WordAlign(ulen);
-  }
+  /* Don't need to hold the URL for a cross referenced image */
 
-  size = ulen;
+  if   (xref >= 0) ulen = 0;
+  else             ulen = strlen(url) + 1;
 
   /* Are we cross-referencing an image which is ready for garbage */
   /* collection? If so, we can just claim this one.               */
@@ -1021,7 +988,39 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
   }
   while (!ok);
 
-  if (ok)
+  /* Throw back an error if out of memory */
+
+  if (!ok)
+  {
+    erb.errnum = Utils_Error_Custom_Message;
+
+    StrNCpy0(erb.errmess,
+             lookup_token("NoMemImg:There is not enough free memory for any new images (%0).",
+                          0,
+                          "1"));
+
+    return &erb;
+  }
+
+  /* Increment the image counter, zero the contents of the image_info structure */
+  /* and fill in some of the fields where contents are known now.               */
+
+  memset(&idata[nimages], 0, sizeof(image_info)); /* Precautionary catch-all */
+
+  idata[nimages].owner      = b;
+  idata[nimages].x          = -1;
+  idata[nimages].y          = -1;
+
+  idata[nimages].token      = token;
+  idata[nimages].last_used  = time(NULL);
+  idata[nimages].xref       = xref;
+
+  idata[nimages].background = !!background;
+  idata[nimages].hadfiller  = 0;
+
+  /* Allocate space for the URL */
+
+  if (ulen)
   {
     tried_once = 0;
 
@@ -1029,19 +1028,14 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
     {
       /* Allocate space for the image data */
 
-      if (ddata)
-      {
-        int oldsize;
+      idata[nimages].flex = malloc(sizeof(image_flex));
 
-        #ifdef TRACE
-          flexcount += sizeof(image_info);
-          if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
-        #endif
+      if (!idata[nimages].flex) ok = 0;
+      else                      ok = 1;
 
-        oldsize = flex_size  ((flex_ptr) &ddata);
-        ok      = flex_extend((flex_ptr) &ddata, oldsize + size);
-      }
-      else ok   = flex_alloc ((flex_ptr) &ddata, size);
+      memset(idata[nimages].flex, 0, sizeof(image_flex));
+
+      if (ok) ok = flex_alloc((flex_ptr) &idata[nimages].flex->urldata, ulen);
 
       if (!ok && !tried_once)
       {
@@ -1053,7 +1047,7 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
     while (!ok);
   }
 
-  /* Throw back an error if out of memory */
+  /* Again, throw back an error if out of memory */
 
   if (!ok)
   {
@@ -1068,30 +1062,10 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
   }
 
   #ifdef TRACE
-    flexcount += size;
+    flexcount += ulen;
     if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
   #endif
 
-  /* Increment the image counter, zero the contents of the image_info structure */
-  /* and fill in some of the fields where contents are known now.               */
-
-  memset(&idata[nimages], 0, sizeof(image_info)); /* Precautionary catch-all */
-
-  idata[nimages].owner      = b;
-  idata[nimages].x          = -1;
-  idata[nimages].y          = -1;
-
-  idata[nimages].alloc      = size;
-  idata[nimages].ualloc     = ulen;
-  idata[nimages].isize      = 0;
-
-  idata[nimages].token      = token;
-  idata[nimages].last_used  = time(NULL);
-  idata[nimages].xref       = xref;
-
-  idata[nimages].background = !!background;
-  idata[nimages].hadfiller  = 0;
-
   /* Set the 'delayed' flag if it's not a background image and */
   /* images are flagged as not being shown right now, or if it */
   /* is a backgrond image and backgrounds are flagged to be    */
@@ -1112,11 +1086,11 @@ _kernel_oserror * image_new_image(browser_data * b, const char * url, HStream *
   }
 
   /* If there's data for a URL, copy it into the above allocated block */
-  /* and ImageLib about the new image.                                 */
+  /* and inform ImageLib of the new image.                             */
 
   if (ulen)
   {
-    strcpy(ddata + image_data_offset(nimages), url);
+    strcpy(idata[nimages].flex->urldata, url);
 
     #ifdef ALL_FAST
       idata[nimages].istore = NewImage(NULL, IMAGE_FAST);
@@ -1216,32 +1190,43 @@ _kernel_oserror * image_process_null(browser_data * b)
            fetches < choices.max_images
          )
       {
-        const char * referer = browser_current_url(b);
-
-        e = html_get(ddata + image_data_offset(image),   /* Document to fetch                */
-                     NULL,                               /* Pointer to any extra data        */
-                     (int *) &idata[image].handle,       /* Place to return the fetch handle */
-                     URL_Method_http_GET,                /* Fetch method                     */
+        const char * referer = browser_base_url(b);
+        char       * url     = malloc(strlen(idata[image].flex->urldata) + 1);
 
-                     #ifndef SINGLE_USER
-                       user.name,                        /* User name for MailServ, if using */
-                     #else                               /* a multiuser version              */
-                       NULL,
-                     #endif
-
-                     0,                                  /* 0 = Don't parse fetched data     */
-                     !b->reloading,                      /* 1 = Proxy can be used, 0 = can't */
-                     referer);                           /* URL of referring page            */
+        if (url)
+        {
+          strcpy(url, idata[image].flex->urldata);
+
+          e = html_get(url,                          /* Document to fetch                */
+                       NULL,                         /* Pointer to any extra data        */
+                       (int *) &idata[image].handle, /* Place to return the fetch handle */
+                       URL_Method_http_GET,          /* Fetch method                     */
+
+                       #ifndef SINGLE_USER
+                         user.name,                  /* User name for MailServ, if using */
+                       #else                         /* a multiuser version              */
+                         NULL,
+                       #endif
+
+                       0,                            /* 0 = Don't parse fetched data     */
+                       !b->reloading,                /* 1 = Proxy can be used, 0 = can't */
+                       referer);                     /* URL of referring page            */
+          free(url);
+        }
+        else e = NULL; /* Suppress compiler warning */
 
-        if (e)
+        if (!url || e)
         {
           /* Don't report it; countless 'I've failed' reports for */
           /* the very beginning of a fetch are not very helpful.  */
-          /* Particularly, 'file not found' for file fetches, can */
-          /* be major pain.                                       */
+          /* Particularly, 'file not found' for file fetches can  */
+          /* be major pain!                                       */
 
           image_abandon(b, image);
+          return NULL;
         }
+
+        url = NULL;
       }
     }
     while (image != lastimage && !idata[image].handle);
@@ -1344,7 +1329,7 @@ _kernel_oserror * image_process_null(browser_data * b)
           /* called, the sprite will plot slowly resulting in       */
           /* (typically) painfully slow redraws.                    */
 
-          if (image == idata[image].owner->background_image)
+          if (image == b->background_image) // idata[image].owner->background_image)
           {
             b->antialias_colour = redraw_backcol(b);
 
@@ -1387,8 +1372,12 @@ _kernel_oserror * image_process_null(browser_data * b)
 
         int       remn, sofar, waiting;
         int       realsize;
+        int       oldsize;
         HStream * tptr;
         void    * store;
+        char    * url = malloc(strlen(idata[image].flex->urldata) + 1);
+
+        if (url) strcpy(url, idata[image].flex->urldata);
 
         /* Go in with no store; only interested in new data, as  */
         /* anything fetched so far goes into the ddata block. */
@@ -1401,28 +1390,32 @@ _kernel_oserror * image_process_null(browser_data * b)
         /* block each time, it won't be able to see the whole header      */
         /* block and the fetch fails (well, the image library keeps       */
         /* waiting for something to happen). At the time of writing this  */
-        /* is demonstrated by the images at http://www.debenhams.co.uk/'. */
+        /* is demonstrated by the images at http://www.debenhams.co.uk/.  */
 
-        if (idata[image].isize)
+        if (idata[image].flex->fetchdata)
         {
-          int p = image_data_offset(image) + idata[image].ualloc;
+          int isize = flex_size(&idata[image].flex->fetchdata);
 
           /* Complain if the flex allocation fails, else copy the data across */
 
-          if (!flex_alloc(&store, idata[image].isize))
+          if (!flex_alloc(&store, isize))
           {
             image_abandon(b, image);
-
+            free(url);
             return NULL;
           }
           else
           {
+            int budge = flex_set_budge(0);
+
             #ifdef TRACE
-              flexcount += idata[image].isize;
+              flexcount += isize;
               if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
             #endif
 
-            memcpy(store, ddata + p, idata[image].isize);
+            memcpy(store, idata[image].flex->fetchdata, isize);
+
+            flex_set_budge(budge);
           }
         }
 
@@ -1460,17 +1453,23 @@ _kernel_oserror * image_process_null(browser_data * b)
 
         /* Call the fetch routine */
 
-        e = html_get_next_token(b,
-                                idata[image].handle,
-                                &remn,
-                                &sofar,
-                                &tptr,
-                                &waiting,
-                                &store,
-                                ddata + image_data_offset(image),
-                                1);
+        if (url)
+        {
+          e = html_get_next_token(b,
+                                  idata[image].handle,
+                                  &remn,
+                                  &sofar,
+                                  &tptr,
+                                  &waiting,
+                                  &store,
+                                  url,
+                                  1);
+
+          free(url);
+        }
+        else e = NULL; /* Suppress compiler warning */
 
-        if (e)
+        if (!url || e)
         {
           /* If there's an error, free the store and ditch the image */
 
@@ -1490,9 +1489,18 @@ _kernel_oserror * image_process_null(browser_data * b)
             if (tl & (1u<<15)) Printf("image_process_null: Exitting with error\n");
           #endif
 
-          return e;
+          /* Only return errors in STRICT_PARSER builds for similar reason to */
+          /* suppression of any errors from html_get above.                   */
+
+          #ifdef STRICT_PARSER
+            return e;
+          #else
+            return NULL;
+          #endif
         }
 
+        url = NULL;
+
         #ifdef TRACE_FETCH_STORE
 
           if (store)
@@ -1527,23 +1535,23 @@ _kernel_oserror * image_process_null(browser_data * b)
 
         /* How much data was added? */
 
-        if (store) realsize = flex_size(&store) - idata[image].isize; /* We already had 'isize' bytes before the fetcher was entered */
-        else       realsize = 0;
+        oldsize  = idata[image].flex->fetchdata ? flex_size(&idata[image].flex->fetchdata) : 0;
+        realsize = store                        ? flex_size(&store) - oldsize              : 0;
 
         /* If we've got data in the local store, copy it to the image store. */
 
-        if (realsize >= 0)
+        if (realsize > 0)
         {
-          int p, oldsize;
+          int ok;
+          int amount = flex_size(&store);
+          int budge;
 
-          /* oldsize holds the whole image data block size (for all images) */
+          /* Make enough room for the extra data */
 
-          oldsize = flex_size((flex_ptr) &ddata);
+          if (idata[image].flex->fetchdata) ok = flex_extend(&idata[image].flex->fetchdata, amount);
+          else                              ok = flex_alloc (&idata[image].flex->fetchdata, amount);
 
-          /* Allocate 'size' extra bytes, doing the relevant error handling */
-          /* code if the allocation fails                                   */
-
-          if (!flex_extend((flex_ptr) &ddata, oldsize + realsize))
+          if (!ok)
           {
             #ifdef TRACE
               flexcount -= flex_size(&store);
@@ -1573,26 +1581,27 @@ _kernel_oserror * image_process_null(browser_data * b)
 
           #endif
 
-          /* p points to the first byte after the data used by this image */
-
-          p = image_data_offset(image) + idata[image].alloc;
+          /* Copy the added data from the temporary store into the image's store */
 
-          /* Move all data from p up to the end of the block (as was, hence */
-          /* oldsize) along the block by size bytes, effectively creating   */
-          /* a gap of that many bytes just after this image's data. If p    */
-          /* = oldsize, this was the last image in the block and there's    */
-          /* no data above it to move (so don't do the memmove).            */
+          budge = flex_set_budge(0);
 
-          if (p != oldsize) memmove(ddata + p + realsize, ddata + p, oldsize - p);
+          memcpy(((char *) idata[image].flex->fetchdata) + oldsize,
+                 ((char *) store)                        + oldsize,
+                 realsize);
 
-          /* Copy the new data into that gap */
+          flex_set_budge(budge);
+        }
 
-          memcpy(ddata + p, ((char *) store) + idata[image].isize, realsize);
+        /* Free the temporary store, if it was used */
 
-          /* Update the image's image data and total data pointers */
+        if (store)
+        {
+          #ifdef TRACE
+            flexcount -= flex_size(&store);
+            if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
+          #endif
 
-          idata[image].alloc += realsize;
-          idata[image].isize += realsize;
+          flex_free(&store);
         }
 
         /* Act according to the fetch status */
@@ -1601,54 +1610,45 @@ _kernel_oserror * image_process_null(browser_data * b)
         {
           case 1: break; /* Nothing happened yet, or the HTTP response header is still coming in */
           case 2: break; /* Redirected, we can ignore and wait for the fetcher to cope */
-          case 0: /* Not awaiting delivery (shouldn't happen in this context, but here 'just in case') */
-          case 3: /* We have some data */
+
+          case 0:        /* Not awaiting delivery (shouldn't happen in this context, but here 'just in case') */
+          case 3:        /* We have some data */
           {
-            int i, n, n1;
+            int offset, remaining, this_block;
 
-            /* Flag that a fetch has started to happen */
+            /* Flag that a fetch is in progress (rather than headers still */
+            /* arriving, waiting, or redirecting)                          */
 
             idata[image].started = 1;
 
-            /* Set 'n' to the size of data that was fetched */
-
-            if (store) n = flex_size(&store);
-            else       n = 0;
-
             /* Increment the 'bytes so far' counter for the image */
 
-            idata[image].bytesgot += n;
+            idata[image].bytesgot += realsize;
 
             /* Push the data into the library, in chunks defined by */
             /* the size of 'buffer' (allocated as a char[] above).  */
+            /* We do this as we can't be sure the ImageLib Load     */
+            /* function is flex-safe, so the recently fetched data  */
+            /* is copiedied into a temporary, non-movable block.    */
 
-            i = 0;
+            offset    = oldsize;
+            remaining = realsize;
 
-            while (n)
+            while (remaining > 0)
             {
-              n1 = n;
+              this_block = remaining;
 
-              if (n1 > sizeof(buffer)) n1 = sizeof(buffer);
-              memcpy(buffer, (char *) store + i, n1);
+              if (this_block > sizeof(buffer)) this_block = sizeof(buffer);
+              memcpy(buffer, ((char *) idata[image].flex->fetchdata) + offset, this_block);
 
-              i += n1;
-              n -= n1;
+              offset    += this_block;
+              remaining -= this_block;
 
-              e = image_load_chunk(b, image, buffer, n1);
+              e = image_load_chunk(b, image, buffer, this_block);
 
               if (e)
               {
-                /* If there's an error, free the store and ditch the image */
-
-                if (store)
-                {
-                  #ifdef TRACE
-                    flexcount -= flex_size(&store);
-                    if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
-                  #endif
-
-                  flex_free(&store);
-                }
+                /* If there's an error, ditch the image */
 
                 image_abandon(b, image);
 
@@ -1668,18 +1668,6 @@ _kernel_oserror * image_process_null(browser_data * b)
           break;
         }
 
-        /* Free the temporary store, if it was used */
-
-        if (store)
-        {
-          #ifdef TRACE
-            flexcount -= flex_size(&store);
-            if (tl & (1u<<14)) Printf("**   flexcount: %d\n",flexcount);
-          #endif
-
-          flex_free(&store);
-        }
-
         /* Closure of long 'if' that deals with either continuing  */
         /* to fetch data for an image, or getting the first chunk  */
         /* of data for a fetch. (The code immediately above is for */
@@ -1704,105 +1692,98 @@ _kernel_oserror * image_process_null(browser_data * b)
 }
 
 /*************************************************/
-/* image_delete_image_entry()                    */
-/*                                               */
-/* Deletes an image_info structure from the      */
-/* array of structures. The cross reference      */
-/* image numbers of subsequent images are        */
-/* adjusted appropriately.                       */
+/* image_mark_as_deleted()                       */
 /*                                               */
-/* The idata flex block is not shrunk; this is   */
-/* expected to be ,one externally, to avoid      */
-/* possibly freeing many small blocks            */
-/* consecutively (which can be slow with flex).  */
-/* E.g. see image_post_deletion_clearup.         */
+/* Mark an image as deleted, but don't remove it */
+/* from the image array yet. When all images     */
+/* that are to be deleted have been thus marked, */
+/* call image_purge_deleted_entries before       */
+/* continuing.                                   */
 /*                                               */
-/* NOTE. Since this removes items from the idata */
-/* array, if you're calling during a for() loop  */
-/* scanning that array, count *backwards* from   */
-/* nimages - 1 to 0. Otherwise, clearly, things  */
-/* will go horribly wrong.                       */
+/* Does not free any memory at all, in the image */
+/* array or any other blocks. Does not cancel    */
+/* fetches or sort out other image references    */
+/* such as background_image fields in            */
+/* browser_data structures either, because this  */
+/* may be called when images are renumbered      */
+/* rather than wholly removed. If completely     */
+/* wiping an image, make sure you do all of that */
+/* *before* calling here.                        */
 /*                                               */
-/* Parameters: Image number to remove.           */
+/* Parameters: Number of the image to mark as    */
+/*             deleted.                          */
 /*************************************************/
 
-static void image_delete_image_entry(int image)
+static void image_mark_as_deleted(int i)
 {
-  browser_data * b;
-  int            i;
+  /* Try to set the flags up so that if for some horrific */
+  /* reason this image should end up being processed as a */
+  /* "live" item, it'll do minimum damage!                */
 
-  nimages--;
-
-  if (lastimage == image) lastimage--;
-  if (lastimage >= nimages) lastimage = nimages - 1;
+  idata[i].handle    = 0;
+  idata[i].istore    = NULL;
 
-  /* Only need to alter xref numbers or move flex data */
-  /* around if this wasn't the last image in the block */
+  idata[i].deleted   = 1;
 
-  if (image < nimages) /* (Not 'nimages - 1' as nimages has been decremented by this point) */
-  {
-    memmove(&idata[image], &idata[image + 1], sizeof(image_info) * (nimages - image));
+  idata[i].fetched   = 1;
+  idata[i].delayed   = 0;
+  idata[i].success   = 1;
 
-    for (i = image; i < nimages; i++)
-    {
-      #ifdef TRACE
-        if (idata[i].xref == image) Printf("JUST DELETED IMAGE %d WHICH WAS XREF'D AND SHOULDN'T HAVE BEEN\n", i);
-      #endif
+  idata[i].hadfirst  = 1;
+  idata[i].reformat  = 0;
+  idata[i].hadfiller = 0;
+  idata[i].canredraw = 0;
+  idata[i].priority  = 0;
 
-      /* Did this cross reference an image which will have changed number? */
-
-      if (idata[i].xref > image) idata[i].xref--;
-
-      /* Update the associated token */
-
-      #ifdef FAST_TOKEN_REFERENCE
-
-        if (idata[i].token)
-        {
-          idata[i].token->flags &= (1 << FAST_TOKEN_REFERENCE_SHIFT) - 1;
-          idata[i].token->flags |= ((i + 1) << FAST_TOKEN_REFERENCE_SHIFT);
-        }
+  idata[i].token     = NULL;
 
-      #endif
-
-      /* Did this have a filler function registered for it? */
-
-      if (idata[i].hadfiller)
-      {
-        /* If so, then provided the filler function is still available, */
-        /* reregister with the new image number.                        */
-
-        if (!idata[i].istore->RegisterFiller)
-        {
-          idata[i].hadfiller = 0;
-        }
-        else
-        {
-          show_error_ret(image_register_filler(i));
-        }
-      }
-    }
-  }
+  return;
+}
 
-  /* Need to also alter browser_data structures' background image */
-  /* number fields.                                               */
+/*************************************************/
+/* image_mark_as_gcable()                        */
+/*                                               */
+/* Mark an image as suitable for garbage         */
+/* collection. Such images may exist in the      */
+/* image array for as long as required and may   */
+/* be reclaimed as working items by calls to     */
+/* image_new_image.                              */
+/*                                               */
+/* Parameters: Number of the image to mark as    */
+/*             suitable for garbage collection.  */
+/*************************************************/
 
-  b = last_browser;
+static void image_mark_as_gcable(int i)
+{
+  idata[i].token = NULL;
 
-  while (b)
-  {
-    if (b->background_image > image) b->background_image--;
+  /* Yup, that was all that needs doing for now */
 
-    b = b->previous;
-  }
+  return;
 }
 
 /*************************************************/
 /* image_discard()                               */
 /*                                               */
-/* Discards all images for a given browser       */
-/* window, freeing the memory and shutting down  */
-/* any active fetches.                           */
+/* For a given browser, discard images:          */
+/*                                               */
+/* * Any images which are xrefed by another      */
+/*   browser have their ownership transferred    */
+/*   to that other browser and the xrefing item  */
+/*   is removed instead                          */
+/*                                               */
+/* * Any images owned by the given browser which */
+/*   just xref other images are removed          */
+/*                                               */
+/* * Any remaining images holding actual image   */
+/*   data are marked for garbage collection but  */
+/*   not removed immediately.                    */
+/*                                               */
+/* When an image is removed, all data, including */
+/* fetches, ImageLib info and external refs to   */
+/* the image number is sorted out. Remaining     */
+/* items in the image_info array may end up      */
+/* renumbered.                                   */
 /*                                               */
 /* Parameters: Pointer to a browser_data struct  */
 /*             relevant to the images.           */
@@ -1813,7 +1794,7 @@ _kernel_oserror * image_discard(browser_data * b)
   int i, xref;
 
   #ifdef TRACE
-    if (tl & (1u<<15)) Printf("\nimage_discard: Called for %p\n\n", b);
+    if (tl & (1u<<15)) Printf("\nimage_discard: Called for %p. There are %d images.\n\n", b, nimages);
   #endif
 
   if (!nimages) return NULL;
@@ -1825,15 +1806,7 @@ _kernel_oserror * image_discard(browser_data * b)
 
   _swix(Hourglass_Start, _IN(0), 1);
 
-  #ifdef TRACE
-    if (tl & (1u<<15))
-    {
-      Printf("image_discard: First pass, checking for cross referencees owned by %p\n", b);
-      Printf("               There are %d images before this pass.\n\n", nimages);
-    }
-  #endif
-
-  /* Must go backwards because we can delete items from the array */
+  /* Must go backwards because we may delete items from the array */
   /* during the loop itself                                       */
 
   for (i = nimages - 1; i >= 0; i--)
@@ -1930,51 +1903,48 @@ _kernel_oserror * image_discard(browser_data * b)
       /* Delete the cross referencer */
 
       #ifdef TRACE
-        if (tl & (1u<<15)) Printf("image_discard: Deleting entry owned by %p that cross references image %d\n", idata[i].owner, idata[i].xref);
+        if (tl & (1u<<15)) Printf("image_discard: Deleting entry owned by %p that cross references image %d\n\n", idata[i].owner, idata[i].xref);
       #endif
 
-      image_remove_all_data(i);
-
-      /* Safe because we're counting through the array backwards */
-
-      image_delete_image_entry(i);
+      image_destroy(i);
     }
-  }
 
-  #ifdef TRACE
-    if (tl & (1u<<15))
-    {
-      Printf("\nimage_discard: First pass complete, there are now %d images\n", nimages);
-      Printf("               Second pass, removing remaining images owned by %p\n\n", b);
-    }
-  #endif
+    /* OK, so this image isn't owned by another browser but cross referencing */
+    /* one owned by ourselves. A simpler case is where we own an image that   */
+    /* just cross references another - we can delete those straight away      */
 
-  /* Now delete any remaining images owned by the given browser */
-  /* - count backwards as we might remove items from the idata  */
-  /* array during the loop.                                     */
-
-  for (i = nimages - 1; i >= 0; i--)
-  {
-    if (idata[i].owner == b)
+    else if (idata[i].owner == b)
     {
-      if (idata[i].istore)
+      if (idata[i].xref >= 0)
       {
-        /* Mark the image as suitable for garbage collection */
+        #ifdef TRACE
+          if (tl & (1u<<15)) Printf("image_discard: Image %d is a simple cross-referencer, deleting it\n\n", i);
+        #endif
+
+        image_destroy(i);
+      }
 
-        idata[i].token = NULL;
+      /* Next case; this is owned by us but holds real data. In this case, mark */
+      /* it as suitable for garbage collection but not as deleted.              */
 
+      else if (idata[i].bytesgot)
+      {
         #ifdef TRACE
-          if (tl & (1u<<15)) Printf("\nimage_discard: Image %d marked for GC but not deleted\n", i);
+          if (tl & (1u<<15)) Printf("image_discard: Image %d being marked for GC\n", i);
         #endif
+
+        image_mark_as_gcable(i);
       }
+
+      /* Last case: Not a cross referencer, but has no data yet - get rid of it */
+
       else
       {
-        image_remove_all_data(i);
-        image_delete_image_entry(i);
-
         #ifdef TRACE
-          if (tl & (1u<<15)) Printf("\nimage_discard: Image %d deleted\n", i);
+          if (tl & (1u<<15)) Printf("image_discard: Image does not cross reference but has no bytes fetched for it yet; so deleting it\n\n", i);
         #endif
+
+        image_destroy(i);
       }
     }
   }
@@ -1982,13 +1952,13 @@ _kernel_oserror * image_discard(browser_data * b)
   _swix(Hourglass_Off, 0);
 
   #ifdef TRACE
-    if (tl & (1u<<15)) Printf("\nimage_discard: Second pass complete, there are now %d images\n\n", nimages);
+    if (tl & (1u<<15)) Printf("image_discard: Delete pass complete; there are %d images. Calling clearup function.\n\n", nimages);
   #endif
 
   image_post_deletion_clearup();
 
   #ifdef TRACE
-    if (tl & (1u<<15)) Printf("image_discard: Successful\n\n");
+    if (tl & (1u<<15)) Printf("\nimage_discard: Successful. Post-clearup, there are %d images.\n\n", nimages);
   #endif
 
   return NULL;
@@ -1997,39 +1967,26 @@ _kernel_oserror * image_discard(browser_data * b)
 /*************************************************/
 /* image_destroy()                               */
 /*                                               */
-/* image_discard gets rid of cross referencing   */
-/* images and transfers ownership of items that  */
-/* are owned by the given browser but cross      */
-/* referenced by another to that other. It does  */
-/* not delete actual, non-cross-referencing      */
-/* images owned by the given browser though, but */
-/* instead marks them as suitable for later      */
-/* garbage collection.                           */
-/*                                               */
-/* image_delete_image_entry is used to remove an */
-/* item from the idata array but does not deal   */
-/* with ImageLib data or ongoing fetches.        */
-/*                                               */
-/* To actually kill an image off completely, use */
-/* this function. It's called by the garbage     */
-/* collector image_gc and used to be part of     */
-/* image_discard before the image garbage        */
-/* collection system existed.                    */
-/*                                               */
-/* Be careful what you delete (!) and remember   */
-/* to call image_post_deletion_clearup when all  */
-/* the images you want to get rid of have been   */
-/* removed by this function.                     */
-/*                                               */
-/* NB - this calls image_delete_image_entry, so  */
-/* the same restrictions on scanning through     */
-/* the idata array as for that function apply.   */
+/* Close down any fetch associated with an       */
+/* image, deal with any references to it (e.g.   */
+/* the background_image field of a browser_data  */
+/* structure), and remove all data except for    */
+/* the actual image_info array entry. The item   */
+/* is marked as deleted.                         */
+/*                                               */
+/* Callers should be aware of the need to call   */
+/* image_post_deletion_clearup as soon as        */
+/* possible after a round of image deletions.    */
 /*                                               */
 /* Parameters: Number of the image to remove.    */
 /*************************************************/
 
 static void image_destroy(int image)
 {
+  #ifdef TRACE
+    if (tl & (1u<<15)) Printf("image_destroy: Called for image %d\n", image);
+  #endif
+
   /* If the image was being fetched, close that session */
 
   if (idata[image].handle)
@@ -2061,71 +2018,289 @@ static void image_destroy(int image)
       idata[image].istore -> Delete(idata[image].istore);
 
     #endif
+
+    idata[image].istore = NULL;
   }
 
+  /* Get rid of associated data except the image_info struct */
+
   image_remove_all_data(image);
-  image_delete_image_entry(image);
+
+  /* Clear the background image field of the owner if necessary */
+
+  if (idata[image].owner->background_image == image)
+  {
+    idata[image].owner->background_image = -1;
+  }
+
+  /* Mark the image as deleted */
+
+  image_mark_as_deleted(image);
 
   #ifdef TRACE
-    if (tl & (1u<<15)) Printf("\nimage_destroy: Image %d deleted\n", image);
+    if (tl & (1u<<15)) Printf("image_destroy: Image %d deleted\n\n", image);
   #endif
 }
 
 /*************************************************/
-/* image_post_deletion_clearup()                 */
+/* image_renumber()                              */
 /*                                               */
-/* After deleting a set of images or image_info  */
-/* structures which had no associated data in    */
-/* ImageLib, call this to tidy up - deregister   */
-/* the animation handler, resize the idata flex  */
-/* block, etc.                                   */
+/* Move an image in the image array. Copies      */
+/* over the structure from new position to old   */
+/* and marks the old as deleted.                 */
+/*                                               */
+/* Destination image number must be a deleted    */
+/* image!                                        */
+/*                                               */
+/* Parameters: Image's current number;           */
+/*                                               */
+/*             Image's desired number.           */
+/*                                               */
+/* Returns:    1 if the item was renumbered, 0   */
+/*             if there was a problem (e.g.      */
+/*             parameters out of range,          */
+/*             destination image not deleted,    */
+/*             etc.).                            */
 /*************************************************/
 
-static void image_post_deletion_clearup(void)
+static int image_renumber(int old, int new)
 {
-  int newsize, i;
+  if (
+       old < 0        ||
+       new < 0        ||
+       old >= nimages ||
+       new >= nimages ||
+       old == new
+     )
+     return 0;
 
-  _swix(Hourglass_Start, _IN(0), 1);
+  if (!idata[new].deleted)
+  {
+    #ifdef TRACE
 
-  /* If there is an animation handler present, but no animated */
-  /* images remain, remove that handler.                       */
+      erb.errnum = Utils_Error_Custom_Normal;
 
-  if (animhandler)
-  {
-    int found = 0;
+      sprintf(erb.errmess,
+              "In image_renumber, %d can't be renumbered to %d as the latter has not been deleted!",
+              old,
+              new);
+
+      show_error_ret(&erb);
 
-    #ifdef TRACE
-      if (tl & (1u<<15)) Printf("image_post_deletion_clearup: There is an animation handler present; checking to see if it can be removed\n");
     #endif
 
+    return 0;
+  }
+
+  /* First off, repoint any images which xref this one, if this */
+  /* image has image data (else it can't be xref'd)             */
+
+  if (idata[old].istore)
+  {
+    int i;
+
     for (i = 0; i < nimages; i++)
     {
-      if (idata[i].istore && idata[i].istore->animated)
+      if (idata[i].xref == old) idata[i].xref = new;
+    }
+  }
+
+  /* Sort out background images */
+
+  if (idata[old].owner->background_image == old) idata[old].owner->background_image = new;
+
+  /* Copy the old structure into the new position */
+
+  idata[new] = idata[old];
+
+  /* Reregister filler functions, if necessary */
+
+  if (idata[new].hadfiller)
+  {
+    /* If so, then provided the filler function is still available, */
+    /* reregister with the new image number.                        */
+
+    if (!idata[new].istore->RegisterFiller)
+    {
+      idata[new].hadfiller = 0;
+    }
+    else
+    {
+      show_error_ret(image_register_filler(new));
+    }
+  }
+
+  /* Update the associated token */
+
+  #ifdef FAST_TOKEN_REFERENCE
+
+    if (idata[new].token)
+    {
+      idata[new].token->flags &= (1 << FAST_TOKEN_REFERENCE_SHIFT) - 1;
+      idata[new].token->flags |= ((new + 1) << FAST_TOKEN_REFERENCE_SHIFT);
+    }
+
+  #endif
+
+  /* Mark the old item as deleted. */
+
+  image_mark_as_deleted(old);
+
+  /* Finished */
+
+  return 1;
+}
+
+/*************************************************/
+/* image_purge_deleted_entries()                 */
+/*                                               */
+/* Gets rid of items in the image array which    */
+/* are marked as deleted by renumbering things   */
+/* to fill holes and then shrinking the image    */
+/* array block.                                  */
+/*                                               */
+/* Assumes nimages is set up to include all of   */
+/* the image array entries (i.e. it hasn't been  */
+/* decremented yet) and decrements it before     */
+/* exitting.                                     */
+/*                                               */
+/* Images marked as deleted but still in the     */
+/* image array MUST ALWAYS BE REMOVED by this    */
+/* function before anything else goes on after a */
+/* round of deleting things. Deleted images will */
+/* cause significant problems if run through the */
+/* rest of the image system...!                  */
+/*                                               */
+/* This function does not touch any allocation   */
+/* blocks other than idata, holding the array of */
+/* image_info structures. The caller should      */
+/* clean up any other image related data when    */
+/* going through images marking them as deleted. */
+/*                                               */
+/* Returns:    1 if any items were deleted (so   */
+/*             nimages will have been            */
+/*             decremented appropriately), else  */
+/*             0.                                */
+/*************************************************/
+
+static int image_purge_deleted_entries(void)
+{
+  int first_space = -1;
+  int last_live   = -1;
+  int finished    = 0;
+
+  int i;
+  int newsize;
+
+  #ifdef TRACE
+    if (tl && (1u<<15)) Printf("image_purge_deleted_entries: Called\n");
+  #endif
+
+  /* Shuffle non-deleted items from higher in the image */
+  /* array down into free places currently held by      */
+  /* deleted items lower down. On the whole, we're      */
+  /* likely to shuffle less data and renumber fewer     */
+  /* images this way than by a dumb "shuffle data down  */
+  /* over the deleted hole" approach.                   */
+
+  do
+  {
+    /* Find the first deleted item in the array */
+
+    for (
+          i = first_space == -1 ? 0 : first_space;
+          i < nimages;
+          i++
+        )
+    {
+      if (idata[i].deleted)
       {
-        found = 1;
+        first_space = i;
         break;
       }
     }
 
-    if (!found)
+    if (first_space == -1)
     {
       #ifdef TRACE
-        if (tl & (1u<<15)) Printf("image_post_deletion_clearup: Removing animation handler\n\n");
+        if (tl && (1u<<15)) Printf("image_purge_deleted_entries: No images are marked as deleted, exitting\n");
       #endif
 
-      deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) image_animate_images, NULL);
-      animhandler = 0;
+      return 0;
     }
 
-    #ifdef TRACE
+    /* Find the last non-deleted (live) item in the array */
+
+    for (
+          i = last_live == -1 ? (nimages - 1) : last_live;
+          i >= 0;
+          i--
+        )
+    {
+      if (!idata[i].deleted)
+      {
+        last_live = i;
+        break;
+      }
+    }
+
+    /* If we can find a live image, proceed */
+
+    if (last_live != -1)
+    {
+      if (first_space > last_live)
+      {
+        /* If the first space is above the last live item, we */
+        /* can just shrink the array.                         */
+
+        nimages = first_space; /* I.e. last_live + 1 */
+
+        #ifdef TRACE
+
+          if (first_space - last_live != 1)
+          {
+            erb.errnum = Utils_Error_Custom_Normal;
+
+            StrNCpy0(erb.errmess,
+                     "You're imagining this error from image_purge_deleted_entries, because it is impossible for it to occur.");
+
+            show_error_ret(&erb);
+          }
 
+        #endif
+
+        finished = 1;
+      }
       else
       {
-        if (tl & (1u<<15)) Printf("image_post_deletion_clearup: The handler is still required\n\n");
+        /* If the first space is below the last live item, must start */
+        /* moving things around. Ho hum.                              */
+
+        #ifdef TRACE
+          if (tl && (1u<<15)) Printf("image_purge_deleted_entries: Renumbering %d to %d\n", last_live, first_space);
+        #endif
+
+        image_renumber(last_live, first_space);
       }
+    }
 
-    #endif
+    /* If there are no live images, can exit the loop */
+
+    else
+    {
+      #ifdef TRACE
+        if (tl && (1u<<15)) Printf("image_purge_deleted_entries: There are no images left\n");
+      #endif
+
+      nimages  = 0;
+      finished = 1;
+    }
   }
+  while (!finished);
+
+  /* Check lastimage is OK */
+
+  if (lastimage >= nimages) lastimage = nimages - 1;
 
   /* Ensure the image_info data block matches the size of the images now present */
 
@@ -2144,10 +2319,85 @@ static void image_post_deletion_clearup(void)
 
   if (idata)
   {
-    if (newsize) flex_extend((flex_ptr) &idata, newsize);
-    else         flex_free((flex_ptr) &idata);
+    if (newsize) flex_extend ((flex_ptr) &idata, newsize);
+    else         flex_free   ((flex_ptr) &idata);
   }
 
+  #ifdef TRACE
+    if (tl && (1u<<15)) Printf("image_purge_deleted_entries: Successful, nimages now %d\n", nimages);
+  #endif
+
+  return 1;
+}
+
+/*************************************************/
+/* image_post_deletion_clearup()                 */
+/*                                               */
+/* After deleting a set of images or image_info  */
+/* structures which had no associated data in    */
+/* ImageLib, call this to tidy up - deregister   */
+/* the animation handler, resize the idata flex  */
+/* block, etc.                                   */
+/*************************************************/
+
+static void image_post_deletion_clearup(void)
+{
+  int i;
+
+  #ifdef TRACE
+    if (tl & (1u<<15)) Printf("image_post_deletion_clearup: Called\n");
+  #endif
+
+  _swix(Hourglass_Start, _IN(0), 1);
+
+  /* Compact the image_info array and shrink the idata flex block; */
+  /* if the function returns 0 we've no work to do here            */
+
+  if (image_purge_deleted_entries())
+  {
+    /* If there is an animation handler present, but no animated */
+    /* images remain, remove that handler.                       */
+
+    if (animhandler)
+    {
+      int found = 0;
+
+      #ifdef TRACE
+        if (tl & (1u<<15)) Printf("image_post_deletion_clearup: There is an animation handler present; checking to see if it can be removed\n");
+      #endif
+
+      for (i = 0; i < nimages; i++)
+      {
+        if (idata[i].istore && idata[i].istore->animated)
+        {
+          found = 1;
+          break;
+        }
+      }
+
+      if (!found)
+      {
+        #ifdef TRACE
+          if (tl & (1u<<15)) Printf("image_post_deletion_clearup: Removing animation handler\n\n");
+        #endif
+
+        deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) image_animate_images, NULL);
+        animhandler = 0;
+      }
+
+      #ifdef TRACE
+
+        else
+        {
+          if (tl & (1u<<15)) Printf("image_post_deletion_clearup: The handler is still required\n\n");
+        }
+
+      #endif
+    }
+  }
+
+  /* Trace build sanity check */
+
   #ifdef FAST_TOKEN_REFERENCE
     #ifdef TRACE
 
@@ -2169,6 +2419,10 @@ static void image_post_deletion_clearup(void)
 
   _swix(Hourglass_Off, 0);
 
+  #ifdef TRACE
+    if (tl & (1u<<15)) Printf("image_post_deletion_clearup: Successful\n");
+  #endif
+
   return;
 }
 
@@ -2187,6 +2441,12 @@ static void image_post_deletion_clearup(void)
 /* caller must remember to invoke                */
 /* image_post_deletion_clearup.                  */
 /*                                               */
+/* It is assumed that for any images marked as   */
+/* suitable for GC, the owner browser still      */
+/* exists. Consequently, this function MUST be   */
+/* called for a given browser if that browser is */
+/* about to be shut down.                        */
+/*                                               */
 /* Parameters: Pointer to a browser_data struct  */
 /*             relevant to the images to collect */
 /*             or NULL to remove any images      */
@@ -2227,8 +2487,6 @@ _kernel_oserror * image_gc(browser_data * b, unsigned int uid, int remove_js_too
         clearup = 1;
       }
     }
-
-    if (clearup) image_post_deletion_clearup();
   }
 
   /* Find the given unique ID */
@@ -2288,6 +2546,8 @@ _kernel_oserror * image_gc(browser_data * b, unsigned int uid, int remove_js_too
     #endif
   }
 
+  if (clearup) image_post_deletion_clearup();
+
   return NULL;
 }
 
@@ -3028,8 +3288,8 @@ static _kernel_oserror * image_update_image(browser_data * b, int image, BBox *
           {
             /* Ignore errors, this isn't vital */
 
-            imghistory_record((const char **) &ddata,
-                              image_data_offset(i),
+            imghistory_record((const char **) &idata[i].flex->urldata,
+                              0,
                               idata[i].istore->width_os,
                               idata[i].istore->height_os);
           }
@@ -3088,8 +3348,8 @@ static _kernel_oserror * image_update_image(browser_data * b, int image, BBox *
               {
                 /* Ignore errors, this isn't vital */
 
-                imghistory_record((const char **) &ddata,
-                                  image_data_offset(i),
+                imghistory_record((const char **) &idata[i].flex->urldata,
+                                  0,
                                   idata[i].istore->width_os,
                                   idata[i].istore->height_os);
               }
@@ -3407,23 +3667,10 @@ _kernel_oserror * image_fill_background(void * handle, int * i)
   /* The image may exist in a table, and we should check what */
   /* background colour to draw if this is the case.           */
 
-  if (idata[image].token && idata[image].token->parent)
+  if (idata[image].token && b->use_source_cols)
   {
-    table_headdata * head  = idata[image].token->parent;
-    table_row      * row   = NULL;
-    table_stream   * table = NULL;
-
-    if (head) row   = head->parent;
-    if (row)  table = row->parent;
-
-    if (table)
-    {
-      if (TABLE_HAS_BGCOL(table)) bgcolour = TABLE_BGCOL(table), noplot = 1;
-
-      /* Individual cells can override the table */
-
-      if (TD_HAS_BGCOL(head))     bgcolour = TD_BGCOL(head),     noplot = 1;
-    }
+    int effective_bgcolour = tokenutils_background_colour(b, idata[image].token);
+    if (effective_bgcolour > -1) bgcolour = effective_bgcolour, noplot = 1;
   }
 
   /* We may still have no defined background colour, in which */
@@ -3551,6 +3798,7 @@ _kernel_oserror * image_fill_background(void * handle, int * i)
     float tempx,  tempy;
     float tempw,  temph;
     int   htop;
+    int   oimage;
 
     #ifdef TRACE
       if (tl & (1u<<15)) Printf("image_fill_background: Plotting scaled background\n");
@@ -3602,12 +3850,18 @@ _kernel_oserror * image_fill_background(void * handle, int * i)
     {
       for (x = redraw.xmin; x <= redraw.xmax; x += w)
       {
+        oimage          = image_redrawing;
+        image_redrawing = bimage;
+
         e = (idata[bimage].istore -> Render(idata[bimage].istore,
                                             x,
                                             y,
                                             100,
                                             w,
                                             h));
+
+        image_redrawing = oimage;
+
         if (e) break;
       }
 
@@ -3620,6 +3874,7 @@ _kernel_oserror * image_fill_background(void * handle, int * i)
   {
     BBox redraw;
     int  htop;
+    int  oimage;
 
     #ifdef TRACE
       if (tl & (1u<<15)) Printf("image_fill_background: Plotting unscaled background\n");
@@ -3654,12 +3909,17 @@ _kernel_oserror * image_fill_background(void * handle, int * i)
     {
       for (x = redraw.xmin; x <= redraw.xmax; x += w)
       {
+        oimage          = image_redrawing;
+        image_redrawing = bimage;
+
         e = (idata[bimage].istore -> Render(idata[bimage].istore,
                                             x,
                                             y,
                                             100,
                                             -1,
                                             -1));
+        image_redrawing = oimage;
+
         if (e) break;
       }
 
@@ -3716,11 +3976,10 @@ _kernel_oserror * image_redraw(browser_data * b, WimpRedrawWindowBlock * r, HStr
   /* image for the given token, which may cross reference 'image', which */
   /* holds the actual render data.                                       */
 
-  image = image_get_token_image_actual(b, token);
+  image = actual = image_get_token_image_actual(b, token);
 
   if (image < 0) return NULL;
 
-  actual = image;
   if (idata[actual].xref >= 0) image = idata[actual].xref;
 
   /* If the image has a fetched width and height, */
@@ -3890,7 +4149,7 @@ int image_tile_window(browser_data * b, WimpRedrawWindowBlock * r, int xorigin,
     for (x = xmin; x <= r->redraw_area.xmax; x += w)
     {
       browser_redrawing = b;
-      image_redrawing   = image;
+      image_redrawing   = b->background_image;
 
       e = idata[image].istore -> Render(idata[image].istore,
                                         x,
@@ -4055,38 +4314,6 @@ _kernel_oserror * image_mode_change(void)
   return NULL;
 }
 
-/*************************************************/
-/* image_data_offset()                           */
-/*                                               */
-/* Returns the offset into the ddata block of    */
-/* the data associated with a given image.       */
-/*                                               */
-/* Parameters: An image number, from 0 to        */
-/*             nimages - 1.                      */
-/*                                               */
-/* Returns:    Pointer to the data associated    */
-/*             with that image (as a char *).    */
-/*************************************************/
-
-static int image_data_offset(int image)
-{
-  int i, count = 0;
-
-  #ifdef TRACE
-    if (tl & (1u<<15)) Printf("image_data_offset: Called for image %d\n",image);
-  #endif
-
-  if (image <= 0) return 0; /* Image 0's data offset will be zero... */
-
-  for (i = 0; i < image; count += idata[i].alloc, i++);
-
-  #ifdef TRACE
-    if (tl & (1u<<15)) Printf("image_data_offset: Successful\n");
-  #endif
-
-  return count;
-}
-
 /*************************************************/
 /* image_count_fetches()                         */
 /*                                               */
@@ -4263,7 +4490,7 @@ int image_total_bytes_fetched(browser_data * b)
 
     for (image = 0; image < nimages; image++)
     {
-      if (idata[image].owner == b) count += idata[image].bytesgot;
+      if (idata[image].owner == b && idata[image].token) count += idata[image].bytesgot;
     }
   }
 
@@ -4501,7 +4728,7 @@ static int image_get_token_image_xref(browser_data * b, HStream * token)
     /* is the one with render data attached, not    */
     /* just a cross reference image.                */
 
-    if (found >= 0)
+    if (found >= 0 && found < nimages)
     {
       if (idata[found].xref >= 0) found = idata[found].xref;
     }
@@ -4870,8 +5097,8 @@ static _kernel_oserror * image_get_image_size(browser_data * b, int image, BBox
 
   if (real_width <= 0 && real_height <= 0)
   {
-    imghistory_return_size((const char **) &ddata,
-                           image_data_offset(actual),
+    imghistory_return_size((const char **) &idata[actual].flex->urldata,
+                           0,
                            &real_width,
                            &real_height);
   }
@@ -5300,7 +5527,6 @@ int image_set_token_image_position(browser_data * b, HStream * t, int x, int y)
 
 void image_get_background_image_url(browser_data * b, char * buffer, int size)
 {
-  int offset;
   int old_budge;
   int image = b ? b->background_image : -1;
 
@@ -5310,17 +5536,13 @@ void image_get_background_image_url(browser_data * b, char * buffer, int size)
 
   if (size < 2 || image < 0 || image >= nimages) return;
 
-  /* Find the offset of the URL in the 'ddata' block */
-
-  offset = image_data_offset(image);
-
   /* Lock flex */
 
   old_budge = flex_set_budge(0);
 
   /* Copy the URL in, ensuring termination */
 
-  strncpy(buffer, ddata + offset, size);
+  strncpy(buffer, idata[image].flex->urldata, size);
   buffer[size - 1] = 0;
 
   /* Restore flex's previous budge state */
@@ -5367,7 +5589,7 @@ void image_convert_to_pixels(browser_data * b, HStream * token, int * x, int * y
   /* If the library has got enough information to fill in the */
   /* width of the image, proceed with the conversion          */
 
-  if (image >= 0 && idata[image].istore->width > 0)
+  if (image >= 0 && idata[image].istore && idata[image].istore->width > 0)
   {
     *x = *x * idata[image].istore->width  / idata[image].istore->width_os;
     *y = *y * idata[image].istore->height / idata[image].istore->height_os;
@@ -5557,7 +5779,8 @@ int image_token_can_be_saved_as_sprite(browser_data * b, HStream * image)
 
 _kernel_oserror * image_export_sprite(char * path, browser_data * b, HStream * image)
 {
-  int i;
+  int               im, i;
+  _kernel_oserror * e;
 
   if (!path || !*path) return NULL;
 
@@ -5565,8 +5788,11 @@ _kernel_oserror * image_export_sprite(char * path, browser_data * b, HStream * i
 
   /* Find the image number */
 
-  if (!image) i = b->background_image;
-  else        i = image_get_token_image_xref(b, image);
+  if (!image) im = b->background_image;
+  else        im = image_get_token_image_actual(b, image);
+
+  if (idata[im].xref >= 0) i = idata[im].xref;
+  else                     i = im;
 
   /* Must have image data... */
 
@@ -5588,9 +5814,17 @@ _kernel_oserror * image_export_sprite(char * path, browser_data * b, HStream * i
 
   /* Export the image */
 
-  return idata[i].istore -> DumpSprite(idata[i].istore,
-                                       path,
-                                       -1);
+  browser_redrawing = b;
+  image_redrawing   = im;
+
+  e = idata[i].istore -> DumpSprite(idata[i].istore,
+                                    path,
+                                    -1);
+
+  browser_redrawing = NULL;
+  image_redrawing   = -1;
+
+  return e;
 }
 
 /*************************************************/
@@ -5637,7 +5871,7 @@ int image_sprite_size(browser_data * b, HStream * image)
 _kernel_oserror * image_export_original(char * path, browser_data * b, HStream * image)
 {
   char url[Limits_URL];
-  int  i, offset;
+  int  i;
 
   if (!path || !*path) return NULL;
 
@@ -5650,9 +5884,7 @@ _kernel_oserror * image_export_original(char * path, browser_data * b, HStream *
 
   /* Must have image data... */
 
-  offset = image_data_offset(i);
-
-  if (!ddata[offset])
+  if (!idata[i].flex->urldata)
   {
     #ifdef TRACE
 
@@ -5670,7 +5902,7 @@ _kernel_oserror * image_export_original(char * path, browser_data * b, HStream *
 
   /* Start a fetch for this URL */
 
-  strncpy(url, ddata + offset, sizeof(url));
+  strncpy(url, idata[i].flex->urldata, sizeof(url));
   url[sizeof(url) - 1] = 0;
 
   RetError(windows_create_browser(url,
@@ -5886,9 +6118,6 @@ int image_to_draw_file(browser_data * b, WimpRedrawWindowBlock * r, HStream * to
 
 //    int               image_size = 0;
 
-    browser_redrawing = b;
-    image_redrawing   = actual;
-
     if (!dont_create || !unique_name)
     {
       unique_name = malloc(Limits_OS_Pathname);
@@ -5899,9 +6128,15 @@ int image_to_draw_file(browser_data * b, WimpRedrawWindowBlock * r, HStream * to
 
         /* Output the sprite, skipping the first 12 bytes of sprite area header */
 
+        browser_redrawing = b;
+        image_redrawing   = actual;
+
         e = idata[image].istore -> DumpSprite(idata[image].istore,
                                               unique_name,
                                               -1);
+
+        browser_redrawing = NULL;
+        image_redrawing   = -1;
       }
     }
 
@@ -5957,6 +6192,9 @@ int image_to_draw_file(browser_data * b, WimpRedrawWindowBlock * r, HStream * to
       unique_name = NULL;
     }
 
+//    browser_redrawing = b;
+//    image_redrawing   = actual;
+//
 //    e       = idata[image].istore -> StartExport(idata[image].istore, &image_size);
 //    ok      = 1;
 //
@@ -5974,12 +6212,12 @@ int image_to_draw_file(browser_data * b, WimpRedrawWindowBlock * r, HStream * to
 //    }
 //
 //    e = idata[image].istore -> EndExport(idata[image].istore);
+//
+//    browser_redrawing = NULL;
+//    image_redrawing   = -1;
 
     /* Flag if we succeeded */
 
-    browser_redrawing = NULL;
-    image_redrawing   = -1;
-
     if (!e && ok) plotted = 1;
   }
 
@@ -6070,9 +6308,15 @@ int image_draw_file_size(browser_data * b, HStream * token, int dont_delete)
     {
       protocols_util_make_unique_name(unique_name, Limits_OS_Pathname);
 
+      browser_redrawing = b;
+      image_redrawing   = actual;
+
       e = idata[image].istore -> DumpSprite(idata[image].istore,
                                             unique_name,
                                             -1);
+
+      browser_redrawing = NULL;
+      image_redrawing   = -1;
     }
 
     if (!e && unique_name)
@@ -6164,8 +6408,8 @@ int image_tile_to_draw(browser_data * b, WimpRedrawWindowBlock * r, int xorigin,
 
   /* Work out the coordinates over which to tile the image */
 
-  xmin = coords_x_toworkarea(r->redraw_area.xmin,r);
-  ymax = coords_y_toworkarea(r->redraw_area.ymax,r);
+  xmin = coords_x_toworkarea(r->redraw_area.xmin, r);
+  ymax = coords_y_toworkarea(r->redraw_area.ymax, r);
 
   xmin -= ((xmin - xorigin) % w);
   ymax -= ((ymax - yorigin) % h) + 1;
@@ -6195,10 +6439,16 @@ int image_tile_to_draw(browser_data * b, WimpRedrawWindowBlock * r, int xorigin,
 
   protocols_util_make_unique_name(name, sizeof(name));
 
+  browser_redrawing = b;
+  image_redrawing   = b->background_image;
+
   e = idata[image].istore -> DumpSprite(idata[image].istore,
                                         name,
                                         -1);
 
+  browser_redrawing = NULL;
+  image_redrawing   = -1;
+
   if (!e)
   {
     e = _swix(OS_File,
@@ -6270,14 +6520,7 @@ int image_tile_to_draw(browser_data * b, WimpRedrawWindowBlock * r, int xorigin,
 
           /* Write out the image */
 
-          browser_redrawing = b;
-          image_redrawing   = image;
-
           ok = savedraw_write_bytes(block, image_size);
-
-          browser_redrawing = NULL;
-          image_redrawing   = -1;
-
           if (!ok) goto image_tile_to_draw_exit;
 
           #ifdef STRICT_PARSER
diff --git a/c/JSwindow b/c/JSwindow
index 064e477dd4b8d55ee8c6b4f37b2dc3e136ed6a79..087107433490a8851595b398f369fffc4a75e46f 100644
--- a/c/JSwindow
+++ b/c/JSwindow
@@ -391,7 +391,6 @@
 
     jsdocument_initialise_class(b);
     jsdocument_define_document(b);
-
     jsurl_define_location(b);
 
 //    JSObject *obj;
@@ -1215,7 +1214,11 @@
 
       case WIN_OPENER:
       {
-        if (decoder->opener && !JSVAL_TO_OBJECT(*vp)) decoder->opener = NULL;
+        if (
+             decoder->opener       &&
+             !JSVAL_TO_OBJECT(*vp)
+           )
+           decoder->opener = NULL;
       }
       break;
 
diff --git a/c/JavaScript b/c/JavaScript
index 8ac2f27e358190a4c59bba656d518d5b817655a5..ee87385ace95719c4eaea2c3df27980efc39f7eb 100644
--- a/c/JavaScript
+++ b/c/JavaScript
@@ -114,6 +114,8 @@ _kernel_oserror * javascript_body_onload(browser_data * b)
     if (tl & (1u<<24)) Printf("javascript_body_onload: Called\n");
   #endif
 
+  if (!b->onload || !*b->onload) return NULL;
+
   #ifdef CUSTOMER_SPECIAL
 
     /* Only call customer_ functions if on the customer's site */
@@ -125,7 +127,19 @@ _kernel_oserror * javascript_body_onload(browser_data * b)
 
   #endif
 
-  b->onload = NULL;
+  /* This is a bit unusual as the Customer hack existed before any real */
+  /* JS support, hence the function itself being 'live' even in non JS  */
+  /* builds.                                                            */
+
+  #ifdef JAVASCRIPT
+
+    e = javascript_process_script(b, b->onload);
+
+  #endif
+
+  /* Prevent multiple script executions... */
+
+  b->onload = "";
 
   return e;
 }
diff --git a/c/Reformat b/c/Reformat
index 25ea8640185e7e4946eae623b34031e9e26e72c4..5dd94f7f7b11a201228cea35e5b4cd5c52a26108 100644
--- a/c/Reformat
+++ b/c/Reformat
@@ -2866,6 +2866,24 @@ static int reformat_reformatter_r(unsigned int flags, browser_data * b, reformat
 
           if (!newchunks) bottom = y;
 
+          /* If this is the first line for a whole page, see if we should */
+          /* now clear the background colour and image                    */
+
+          if (!d->table && !d->nlines)
+          {
+            if (!b->onload && !b->onunload)
+            {
+              b->background_colour = -1;                /* Background colour, or -1 for default  */
+              b->background_image  = -1;                /* Image no. of background image, 0=none */
+              b->antialias_colour  = redraw_backcol(b); /* Colour to anti-alias to, or -1=none   */
+
+              browser_update_bottom(b, 0);
+
+              b->onload            = "";
+              b->onunload          = "";
+            }
+          }
+
           /* Add the new line structure */
 
           e = reformat_add_line(b, d);
diff --git a/c/TokenUtils b/c/TokenUtils
index b86b3f7c5808fba89409e1308d219ab6c541aeb3..13c6f34eb587e153892abf35082abc15ac309507 100644
--- a/c/TokenUtils
+++ b/c/TokenUtils
@@ -1025,3 +1025,51 @@ int tokenutils_within_distance(browser_data * b, HStream * t1, HStream * t2, int
 
   return (check <= distance);
 }
+
+/*************************************************/
+/* tokenutils_background_colour()                */
+/*                                               */
+/* See what background colour should be used for */
+/* a body text token; deals with tables, going   */
+/* down through the structure until something    */
+/* with a background colour is found (just       */
+/* checking the immediate local table or cell is */
+/* not good enough as a background colour from   */
+/* an outer level table as part of a nested set  */
+/* of cells may be what is showing through).     */
+/*                                               */
+/* Parameters: Pointer to a browser_data struct  */
+/*             relevant to the token;            */
+/*                                               */
+/*             Pointer to the HStream struct     */
+/*             who's background colour should be */
+/*             returned.                         */
+/*                                               */
+/* Returns:    Background colour as BBGGRR00,    */
+/*             or -1 if none was found (so you   */
+/*             should use redraw_backcol() or    */
+/*             any page background image).       */
+/*************************************************/
+
+int tokenutils_background_colour(browser_data * b, HStream * t)
+{
+  while (t && t->parent)
+  {
+    table_headdata * head  = t->parent;
+    table_row      * row   = NULL;
+    table_stream   * table = NULL;
+
+    if (head) row   = head->parent;
+    if (row)  table = row->parent;
+
+    if (table)
+    {
+      if (TD_HAS_BGCOL(head))     return TD_BGCOL(head);
+      if (TABLE_HAS_BGCOL(table)) return TABLE_BGCOL(table);
+    }
+
+    t = (HStream *) table;
+  }
+
+  return -1;
+}
diff --git a/c/URLutils b/c/URLutils
index 2b534a9b0629a9f91491a8ee969d32c335e8bb86..c75af6a6bbc0479c7ab325ea4ce54a33d38bcfaa 100644
--- a/c/URLutils
+++ b/c/URLutils
@@ -1804,7 +1804,7 @@ void urlutils_set_displayed(browser_data * b, char * iurl)
 }
 
 /*************************************************/
-/* Urlutils_check_protocols()                    */
+/* urlutils_check_protocols()                    */
 /*                                               */
 /* Checks a given URL to see if the fetch        */
 /* protocol it specifies can be handled.         */
diff --git a/c/Windows b/c/Windows
index 9e7f9526150bae4a0a0c36238cbf90e7e6154f5f..27da60e26212db62fa6b5d7c37a3e320c45534b4 100644
--- a/c/Windows
+++ b/c/Windows
@@ -531,9 +531,18 @@ _kernel_oserror * windows_create_browser(const char * url, browser_data * real_p
 
     #ifdef JAVASCRIPT
 
-      /* Get an execution context for this window */
+      if (save_type == Windows_CreateBrowser_AsJSChild && real_parent)
+      {
+        /* Get an execution context using the parent */
+
+        RetError(javascript_gain_context(b, real_parent));
+      }
+      else
+      {
+        /* Get an execution context using any ancestor */
 
-      RetError(javascript_gain_context(b, b->ancestor));
+        RetError(javascript_gain_context(b, b->ancestor));
+      }
 
     #endif
 
@@ -1129,11 +1138,16 @@ int windows_open_browser(int eventcode, WimpPollBlock * b, IdBlock * idb, browse
         }
         else
         {
-          b->open_window_request.visible_area.xmin = s.visible_area.xmin;
-          b->open_window_request.visible_area.xmax = s.visible_area.xmax;
+          int add, rmarg;
+
+          convert_to_os(choices.right_margin, &rmarg);
+
+          if      (s.visible_area.xmax - s.visible_area.xmin > handle->display_extent) add = s.visible_area.xmax - s.visible_area.xmin;
+          else if (s.visible_area.xmax - s.visible_area.xmin < handle->display_extent) add = handle->display_extent + rmarg * 2; /* To give a reasonable right border gap */
+          else                                                                         add = handle->display_extent;
 
-          w.xmin = s.visible_area.xmin;
-          w.xmax = s.visible_area.xmax;
+          w.xmin = b->open_window_request.visible_area.xmin = s.visible_area.xmin;
+          w.xmax = b->open_window_request.visible_area.xmax = s.visible_area.xmin + add;
         }
 
         b->open_window_request.visible_area.ymin = s.visible_area.ymin - o.outline.ymin + (shift ? 128 : 0);
diff --git a/h/Images b/h/Images
index e5fcfba3e75f75a86679d98fe18601b3b465905a..4a9ea737d1708aef87819066defceac9892bbd25 100644
--- a/h/Images
+++ b/h/Images
@@ -42,6 +42,18 @@
 #define ImageDefaultOSSize_X 48
 #define ImageDefaultOSSize_Y 48
 
+/* The image_flex structure is used to hold flex anchors. It is  */
+/* attached to image_info (below) in a small malloc block; since */
+/* image_info structs are held in a flex area they cannot hold   */
+/* the flex anchors directly.                                    */
+
+typedef struct image_flex
+{
+  char * urldata;
+  void * fetchdata;
+}
+image_flex;
+
 /* The image_info structure is used for the image handling code, */
 /* where each image has associated data about its size, fetch    */
 /* status and so-on.                                             */
@@ -59,10 +71,7 @@ typedef struct image_info
   int                   x;              /* X position on the page for the owner                         */
   int                   y;              /* Y position on the page for the owner                         */
 
-  int                   dataoffset;     /* Offset of image data within image data block                 */
-  int                   alloc;          /* Total space allocated in flex block                          */
-  int                   ualloc;         /* Space allocated to url in flex block                         */
-  int                   isize;          /* Size of image storage used                                   */
+  image_flex          * flex;           /* For URL data, fetch data etc.                                */
   HStream             * token;          /* Token this image appears in - may be NULL (pending GC)       */
   time_t                last_used;      /* Time when the image was last used (for GC purposes)          */
 
@@ -73,6 +82,8 @@ typedef struct image_info
 
   int                   bytesgot;       /* Total bytes fetched for this image so far                    */
 
+  unsigned int          deleted     :1; /* Image is marked as deleted (so remove it from array ASAP!)   */
+
   unsigned int          started     :1; /* The fetch has started to happen                              */
   unsigned int          fetched     :1; /* Flag indicating the image is fetched                         */
   unsigned int          delayed     :1; /* Flag indicating the image is delayed (by delayimages option) */
diff --git a/h/TokenUtils b/h/TokenUtils
index 84b48ad38bb9166e16f908889b4005a98243c1f5..fff04cc90628eb37433b274503ab2f29c3a347f0 100644
--- a/h/TokenUtils
+++ b/h/TokenUtils
@@ -44,3 +44,5 @@ reformat_cell * tokenutils_find_cell          (reformat_cell * cell, int depth,
 void            tokenutils_token_offset       (browser_data * b, token_path * path, int * offset_x, int * offset_y);
 reformat_cell * tokenutils_token_cell         (browser_data * b, HStream * token);
 int             tokenutils_within_distance    (browser_data * b, HStream * t1, HStream * t2, int distance);
+
+int             tokenutils_background_colour  (browser_data * b, HStream * t);