/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : FetchPage.c                            */
/*                                                 */
/* Purpose: High-level fetch functions; the main   */
/*          interface for initiating and control-  */
/*          ling full page fetches. Compare with   */
/*          lower level Fetch.c and FetchHTML.c.   */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 25-Nov-96: Created.                    */
/***************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "flex.h"

#include "swis.h"

#include "URI.h"     /* URI handler API, in URILib:h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"
#include "window.h"
#include "gadgets.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "MiscDefs.h"
#include "Utils.h"

#include "Browser.h"
#include "Fetch.h" /* (Which itself includes URLstat.h) */
#include "Frames.h"
#include "Handlers.h"
#include "History.h"
#include "Images.h"
#include "JavaScript.h"
#include "Memory.h"
#include "PlugIn.h"
#include "Protocols.h"
#include "Reformat.h"
#include "SaveDraw.h"
#include "SaveText.h"
#include "Toolbars.h"
#include "URLutils.h"
#include "Windows.h"

#include "FetchPage.h"

/* Locals */

char * url_buffer = NULL;

/* Static function prototypes */

static _kernel_oserror * fetchpage_process_internal (browser_data * b);
static _kernel_oserror * fetchpage_preprocessed     (browser_data * b, int record, int stop);
static _kernel_oserror * fetchpage_postprocessed    (browser_data * b, int record);

/*************************************************/
/* fetchpage_fetch()                             */
/*                                               */
/* Handles the initiation of a fetch and the     */
/* display of the result in a browser window.    */
/*                                               */
/* Parameters are as standard for a Wimp event   */
/* handler (this is called on null events).      */
/*************************************************/

int fetchpage_fetch(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle)
{
  _kernel_oserror * e;
  int               tf_start, tf_now, priority;
  int               fetching, formatting;

  #ifdef TRACE
    {
      static oldstatus;

      if ((tl & (1u<<6)) && (handle->fetch_status != oldstatus))
      {
        Printf("\nfetchpage_fetch: Called with new status %d\n",handle->fetch_status);
        oldstatus = handle->fetch_status;
      }
    }
  #endif

  if (handle->fetch_status == BS_START)
  {
    e = fetch_start(handle);

    if (e)
    {
      handle->save_link = 0;
      show_error_cont(e);
    }
  }

  /* Call the fetcher / reformatter, allowing a certain */
  /* amount of time inside each only.                   */

  _swix(OS_ReadMonotonicTime, _OUT(0), &tf_start);
  tf_now = tf_start;

  /* The main fetch/reformat loop */

  fetching   = fetch_fetching(handle);
  formatting = reformat_formatting(handle);

  /* Some fairly crude load balancing. If the fetcher is idle, */
  /* but this function is being called, chances are we're      */
  /* formatting. Otherwise, we may be processing tokens - so   */
  /* the actual fetch is complete but the fetcher is still     */
  /* working on the data from HTMLLib. Otherwise, it may be    */
  /* that the main fetch is active; or we could be spooling    */
  /* data to a file.                                           */

  if (formatting && controls.refo_single) priority = 300;
  else
  {
    if      (handle->fetch_status == BS_IDLE)                           priority = 15; /* Not fetching, may be solid formatting    */
    else if (handle->fetch_status == BS_PROCESS)                        priority = 8;  /* Processing tokens, but fetch is complete */
    else if (handle->fetch_status == BS_FETCHING)                       priority = 3;  /* Fetching tokens (so fetch is incomplete) */
    else if (handle->fetch_status == BS_DATAFETCH && handle->save_file) priority = 2;  /* Saving data to a file (fetch incomplete) */
    else                                                                priority = 0;
  }

  do
  {

    /* If fetching, call the reformatter. */

    if (fetching) fetch_fetcher(handle);

    /* If reformatting, call the reformatter. */

    if (formatting)
    {
      reformat_reformatter(handle);
      ChkError(windows_check_tools(handle, NULL));
    }

    fetching   = fetch_fetching(handle);
    formatting = reformat_formatting(handle);

    /* Re-read the time, and keep going whilst we're   */
    /* inside the maximum time specified by 'priority' */
    /* and fetching and/or formatting.                 */

    _swix(OS_ReadMonotonicTime, _OUT(0), &tf_now);
  }
  while (
          tf_now - tf_start < priority &&
          (fetching || formatting)
        );

  /* Process images on a lower priority */

  if (image_count_specific_pending(handle))
  {
    e = image_process_null(handle);

    /* Force all errors to be warnings only */

    if (e)
    {
      if (&erb != e) erb = *e;
      erb.errnum = Utils_Error_Custom_Message;

      ChkError(&erb);
    }
  }

  /* Handle jumping to any specified named anchors */

  if (handle->display_request == DISPLAY_NAMED)
  {
    char    * p;
    HStream * t;

    t = 0;

    p = fetch_find_name_tag(browser_current_url(handle)) + 1;
    t = fetch_find_anchor_token(handle, p);

    if (t)
    {
      handle->display_request = t;
      handle->display_offset  = 0;
    }
  }
  else
  {
    if (
         handle->display_request &&
         browser_show_token(handle,
                            handle->display_request,
                            handle->display_offset)
       )
    {
      WimpGetWindowStateBlock s;

      s.window_handle = handle->window_handle;
      wimp_get_window_state(&s);

      if (s.yscroll != handle->display_vscroll) handle->display_vscroll = s.yscroll;
      else handle->display_request = 0, handle->display_vscroll = 0;
    }
  }

  /* Various actions as things become inactive... */

  if (!fetch_fetching(handle))
  {
    /* If we have a JavaScript onLoad command, deal with it */

    if (handle->onload) ChkError(javascript_body_onload(handle));

    /* Garbage collect images if the main page fetch has finished */

    if (handle->clear_images)
    {
//      image_discard_unused(handle);
      handle->clear_images = 0;
    }

    if (!reformat_formatting(handle))
    {
      if (!image_count_specific_pending(handle))
      {
        /* There are no pending images, so we seem to have finished - */
        /* but is there a reformat pending?                           */

        if (handle->refo_time)
        {
          /* Yes, so flush the queue */

          reformat_format_from(handle, handle->refo_line, 1, -1);
        }
        else
        {
          /* No; get rid of null claimants (the call will *install* a new */
          /* animation drift handler if the Controls require it).         */

          if (handle->fetch_handler) fetchpage_release_nulls(handle);

          /* We may have a pending messages to send */

          e = protocols_atats_send_any_pendings(handle);

          /* If this is a small fetch window, close it */

          if (handle->small_fetch)
          {
            int close = 1;

            /* If this is a fetch for a Plug-In, tell the Plug-In about it */

            if (
                 handle->pstream             &&
                 handle->pstream->active     &&
                 !handle->pstream->abandoned
               )
            {
              plugin_fetch_completed(handle);

              /* Only close the window if the stream won't close itself */

              if (handle->pstream->will_close_itself) close = 0;
            }

            /* Close the window *afterwards* - or you've  */
            /* just freed up message structures etc. that */
            /* need to be dealt with first.               */

            if (close) windows_close_browser(handle);
          }

          /* Otherwise, proceed as normal */

          else
          {
            /* Check the page's vertical extent is correct */

            ChkError(reformat_check_extent(handle));

            /* Update the status bar */

            toolbars_cancel_all(handle);
            toolbars_update_status(handle, Toolbars_Status_Viewing);
          }

          /* Report any errors from the pending message sends */

          ChkError(e);
        }
      }

      /* Sort out window tool presence */

      ChkError(windows_check_tools(handle, NULL));
    }
  }

  /* Keep the buttons as up to date as possible throughout the fetch */

  toolbars_set_button_states(handle);

  return 0;
}

/*************************************************/
/* fetchpage_fetch_targetted()                   */
/*                                               */
/* Fetch a given URL, possibly appending some    */
/* given extra data, into a given target window, */
/* an ancestor browser, or a new view.           */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             which acts as a parent;           */
/*                                               */
/*             Pointer to the URL to fetch;      */
/*                                               */
/*             Pointer to a window target name,  */
/*             or NULL to open in the parent -   */
/*             if full screen, note that any     */
/*             target names which would          */
/*             otherwise have opened a new       */
/*             window will not do so;            */
/*                                               */
/*             Pointer to any extra data to      */
/*             append to the URL, or NULL;       */
/*                                               */
/*             1 to open the URL in a new window */
/*             (with no name) regardless of the  */
/*             targetting, else 0 (this will not */
/*             be overridden when running full   */
/*             screen).                          */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch.            */
/*************************************************/

_kernel_oserror * fetchpage_fetch_targetted(browser_data * parent, const char * url, const char * target, const char * appnddata, int new)
{
  browser_data * ancestor;
  browser_data * targetted;

  /* If we've been asked to open a new window, only the extra data */
  /* parameter matters.                                            */

  if (new)
  {
    if (appnddata) return fetchpage_new_add(NULL,
                                            url,
                                            1,
                                            1,
                                            appnddata,
                                            1,
                                            NULL);
    else
    {
      RetError(windows_create_browser(url,
                                      NULL,
                                      NULL,
                                      NULL,
                                      Windows_CreateBrowser_Normal));

      RetError(browser_inherit(parent, last_browser));

      return NULL;
    }
  }

  /* Otherwise, have a bit more thinking to do */

  else
  {
    if (!parent) return NULL;

    ancestor = utils_ancestor(parent);

    /* Deal with the targetted window case first */

    if (target && *target)
    {
      targetted = frames_find_target(parent, target);

      if (targetted)
      {
        if (parent != targetted) RetError(browser_inherit_post_data(parent, targetted));

        /* We have a specific browser to open the URL in */

        if (appnddata)
        {
          return fetchpage_new_add(targetted,
                                   url,
                                   1,
                                   1,
                                   appnddata,
                                   0,
                                   NULL);

        }
        else
        {
          return fetchpage_new(targetted,
                               url,
                               1,
                               1);
        }
      }
      else
      {
        /* Nothing was found with that name, so create a new window */
        /* with the given target name instead.                      */

        if (appnddata)
        {
          return fetchpage_new_add(parent,
                                   url,
                                   1,
                                   1,
                                   appnddata,
                                   1,
                                   target);
        }
        else
        {
          RetError(windows_create_browser(url,
                                          NULL,
                                          NULL,
                                          target,
                                          Windows_CreateBrowser_Normal));

          RetError(browser_inherit(parent, last_browser));

          return NULL;
        }
      }
    }

    /* Now the untargetted case - open in the parent */

    else
    {
      if (appnddata) return fetchpage_new_add(parent,
                                              url,
                                              1,
                                              1,
                                              appnddata,
                                              0,
                                              NULL);

      else           return fetchpage_new(parent,
                                          url,
                                          1,
                                          1);
    }
  }
}

/*************************************************/
/* fetchpage_process_internal()                  */
/*                                               */
/* Some internal URLs involve just substituting  */
/* the internal URL for some known or easily     */
/* discoverable alternative early in the fetch   */
/* stage. This function handles such changes.    */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch.            */
/*************************************************/

static _kernel_oserror * fetchpage_process_internal(browser_data * b)
{
  if (
       b->displayed == Display_Recovered_Page ||
       b->displayed == Display_Home_Page      ||
       b->displayed == Display_Embedded_URL
     )
  {
    char alt_url[Limits_URL];

    memset(alt_url, 0, sizeof(alt_url));

    if (b->displayed == Display_Recovered_Page)
    {
      /* For a recovered page, try to get back to the page detailed  */
      /* in Browse$PreviousPage.                                     */
      /*                                                             */
      /* If the variable is unset / can't be read, can't load a page */
      /* so set the buffer to hold a null string.                    */
      /*                                                             */
      /* The call is quivalent to getenv, but the RISC OS            */
      /* implementation evaluates the system variable as an          */
      /* expression which we don't want; hence the direct use of the */
      /* SWI call.                                                   */

      if (
           _swix(OS_ReadVarVal,
                 _INR(0,4),

                 "Browse$PreviousPage",
                 alt_url,
                 sizeof(alt_url),
                 0,
                 4)

         )
         *alt_url = 0;
    }
    else if (b->displayed == Display_Embedded_URL)
    {
      char * fetch = url_buffer;

      if (!fetch) fetch = "";

      /* There may be a specific URL embedded in the item */

      strncpy(alt_url, fetch, sizeof(alt_url) - 1);
      alt_url[sizeof(alt_url) - 1] = 0;

      /* Find the URL fragment */

      fetch = strstr(alt_url, "?url=");

      if (!fetch) *alt_url = 0;
      else
      {
        char * copy = alt_url;

        fetch += sizeof("?url=") - 1;

        /* Copy the URL to the start of the buffer, */
        /* unescaping any escaped characters.       */

        while (*fetch)
        {
          if (*fetch == '%' && *(fetch + 1) && *(fetch + 2))
          {
            char number[3];

            number[0] = *(fetch + 1);
            number[1] = *(fetch + 2);
            number[2] = '\0';

            *(copy ++) = (char) strtoul(number, NULL, 16);

            fetch += 3;
          }
          else *(copy ++) = *(fetch ++);
        }

        *copy = '\0';
      }
    }
    else
    {
      /* Alternatively, get the Home Page URL */

      urlutils_create_home_url(alt_url, sizeof(alt_url));
    }

    /* Ensure the URL is terminated */

    alt_url[sizeof(alt_url) - 1] = 0;

    /* Reallocate URL buffer space */

    if (url_buffer)
    {
      #ifdef TRACE
        malloccount -= strlen(url_buffer) + 128;
        if (tl & (1u<<13)) Printf("** malloccount (fetchpage_process_internal): \0212%d\0217\n",malloccount);
      #endif

      free(url_buffer);
    }

    url_buffer = malloc(strlen(alt_url) + 128);

    if (!url_buffer) return make_no_fetch_memory_error(10);

    #ifdef TRACE
      malloccount += (strlen(alt_url) + 128);
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_process_internal): \0211%d\0217\n",malloccount);
    #endif

    /* Copy the new URL into the buffer */

    strcpy(url_buffer, alt_url);
  }

  return NULL;
}

/*************************************************/
/* fetchpage_preprocessed()                      */
/*                                               */
/* Fetches a URL, which must be in the           */
/* 'url_buffer' malloced block of memory.        */
/* Intended to be called from functions such     */
/* as fetchpage_new or fetchpage_new_add.        */
/*                                               */
/* If using the URI handler, the URL will be     */
/* sent through that and won't actually fetch    */
/* at this stage, therefore.                     */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to which the new URL refers;      */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0;         */
/*                                               */
/*             1 to stop other page fetches in   */
/*             any other frames related to this  */
/*             document, 0 to only stop those in */
/*             this frame.                       */
/*************************************************/

static _kernel_oserror * fetchpage_preprocessed(browser_data * b, int record, int stop)
{
  _kernel_oserror * e;

  if (
       !b->save_link &&
       !b->post_data && /* Must do the fetch even if we could just jump to a named anchor, if a POST */
       !b->reloading && /* form is being sent or if we're deliberately force reloading something.    */
       browser_display_local_reference(b,
                                       url_buffer,
                                       browser_current_url(b))
     )
  {
    // Huh? This just causes redraw glitches... Why was it ever put here?
    //
    // if (choices.keyboard_ctrl) browser_move_selection(b, akbd_RightK);

    return NULL;
  }

  /* Last chance to modify any passing URLs... */

  if (!strcmp(url_buffer, AboutMethod))
  {
    char * alt_url = Internal_URL ForAbout AboutMethod AboutMethod; /* (sic) */

    #ifdef TRACE
      malloccount -= strlen(url_buffer) + 128;
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_preprocessed): \0212%d\0217\n",malloccount);
    #endif

    free(url_buffer);

    url_buffer = malloc(strlen(alt_url) + 128);

    if (!url_buffer) return make_no_fetch_memory_error(10);

    #ifdef TRACE
      malloccount += (strlen(alt_url) + 128);
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_preprocessed): \0211%d\0217\n",malloccount);
    #endif

    /* Copy the new URL into the buffer */

    strcpy(url_buffer, alt_url);
  }

  /* Only cancel stuff if we're allowed to */

  if (b->allow_cancel)
  {
    e = fetch_cancel(b);
    if (e) return e;

    /* If required, stop all fetching in all frames, else leave */
    /* images but stop everything else. Note that we don't      */
    /* touch file fetching - clicking on links will allow those */
    /* to continue, so several frames in a frameset could fetch */
    /* to a file simultaneously (the progress display in the    */
    /* status bar is aware of this, and will alter its display  */
    /* appropriately).                                          */

    if (stop)
    {
      if (controls.brick_wall) frames_abort_fetching(utils_ancestor(b), 1, 0);
      else                     frames_abort_fetching(utils_ancestor(b), 0, 0);
    }
  }

  /* Set the allow_cancel flag back to 1 for future fetches */

  b->allow_cancel = 1;

  /* Set the displayed type for internal / normal URLs, and */
  /* carry out any required special actions for the former. */

  if (!b->save_link)
  {
    /* Before we reset the display type, check if we're displaying */
    /* a temporary file. If so, force the 'record' flag off, as    */
    /* there's no way this can make a meaningful History entry.    */

    if (b->displayed == Display_Scrap_File) record = 0;

    /* Right, now go ahead and set the display type */

    urlutils_set_displayed(b, url_buffer);

    if (b->displayed == Display_Previous_Page) return history_fetch_backwards(b, 0);
    if (b->displayed == Display_Next_Page)     return history_fetch_forwards(b, 0);

    if (b->displayed == Display_Reloaded_Page)
    {
      IdBlock id;

      id.ancestor_id = b->self_id;

      handle_reload(0, NULL, &id, NULL);

      return NULL;
    }
  }

  e = fetchpage_process_internal(b);
  if (e) return e;

  /* If merging the URL writable and status display, put */
  /* it back to status.                                  */

  if (b->merged_url)
  {
    toolbars_merged_to_status(b, toolbars_get_upper(b));
    browser_give_general_focus(b);
  }

  if (uri_module_present && strncmp(url_buffer, Internal_URL, Int_URL_Len))
  {
    /* Send the URL through the URI handler if the module is present */
    /* and the URL isn't an internal one.                            */

    return urlutils_dispatch(b,
                             url_buffer,
                             record ? URIQueue_RecordInHistory : 0);
  }
  else
  {
    /* Without the URI handler, deal with the URL immediately */

    return fetchpage_postprocessed(b, record);
  }
}

/*************************************************/
/* fetchpage_postprocessed()                     */
/*                                               */
/* Working end to fetchpage_preprocessed, which  */
/* will fetch the url in the url_buffer block.   */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to which the new URL refers;      */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0.         */
/*************************************************/

static _kernel_oserror * fetchpage_postprocessed(browser_data * b, int record)
{
  _kernel_oserror * e;

  #ifdef TRACE
    if (tl & (1u<<12)) Printf("fetchpage_postprocessed: Chunk CK_FURL set to %d\n",strlen(url_buffer) + 128);
  #endif

  e = memory_set_chunk_size(b, NULL, CK_FURL, strlen(url_buffer) + 128);
  if (e) return e;

  strcpy(b->urlfdata, url_buffer);

  /* Make sure the URL bar is updated with the current URL. */

  toolbars_update_url(b);

  /* Set the fetch status */

  b->fetch_status = BS_START;

  /* Sort out the reloading flag */

  if (b->reload_lock) b->reload_lock = 0;
  else                b->reloading   = 0;

  /* Record the start of the fetch, for a parent browser window. */

  if (!b->ancestor)
  {
    _swix(OS_SetVarVal,
          _INR(0,4),

          "Browse$CurrentFetch",
          url_buffer,
          strlen(url_buffer),
          0,
          4);
  }

  /* Register event handlers to start off the new fetch */

  if (!b->fetch_handler) fetchpage_claim_nulls(b);

  return NULL;
}

/*************************************************/
/* fetchpage_postprocess_uri()                   */
/*                                               */
/* If the URI handler comes back with a          */
/* URI_MProcess message and we can handle the    */
/* URI it details, then that URI may be fetched  */
/* through this function - it is first copied    */
/* locally and then passed over to               */
/* fetchpage_postprocessed.                      */
/*                                               */
/* Parameters: Pointer to a browser_data         */
/*             struct relevant to the URI;       */
/*                                               */
/*             Pointer to the URI string;        */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0.         */
/*************************************************/

_kernel_oserror * fetchpage_postprocess_uri(browser_data * b, char * uri, int record)
{
  /* Reallocate URL buffer space */

  if (url_buffer)
  {
    #ifdef TRACE
      malloccount -= strlen(url_buffer) + 128;
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_postprocess_uri): \0212%d\0217\n",malloccount);
    #endif

    free(url_buffer);
  }

  url_buffer = malloc(strlen(uri) + 128);

  if (!url_buffer) return make_no_fetch_memory_error(14);

  #ifdef TRACE
    malloccount += (strlen(uri) + 128);
    if (tl & (1u<<13)) Printf("** malloccount (fetchpage_postprocess_uri): \0211%d\0217\n",malloccount);
  #endif

  /* Copy the URI over and fetch it */

  strcpy(url_buffer, uri);

  return fetchpage_postprocessed(b, record);
}

/*************************************************/
/* fetchpage_new()                               */
/*                                               */
/* Cancels any old fetch and starts a new one    */
/* the given URL.                                */
/*                                               */
/* The URL is copied to a malloc buffer before   */
/* being used, so the pointer to it can be from  */
/* pretty much anything (though beware of flex   */
/* blocks shifting over the actual function call */
/* boundary...).                                 */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to which the new URL refers;      */
/*                                               */
/*             Pointer to the new URL string;    */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0;         */
/*                                               */
/*             1 to stop other page fetches in   */
/*             any other frames related to this  */
/*             document, 0 to only stop those in */
/*             this frame.                       */
/*************************************************/

_kernel_oserror * fetchpage_new(browser_data * b, const char * url, int record, int stop)
{
  /* Don't proceed unless there's something to fetch */

  if (!url || !(*url)) return fetch_cancel(b);

  /* The URL may have been passed in from the 'tokens' buffer, */
  /* and fetch cancels etc. might corrupt it. So take a copy   */
  /* of it before proceeding further, if the URL didn't come   */
  /* from this buffer already...!                              */

  if (url != url_buffer)
  {
    if (url_buffer)
    {
      #ifdef TRACE
        malloccount -= strlen(url_buffer) + 128;
        if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new): \0212%d\0217\n",malloccount);
      #endif

      free(url_buffer);
    }

    url_buffer = malloc(strlen(url) + 128);

    if (!url_buffer) return make_no_fetch_memory_error(7);

    #ifdef TRACE
      malloccount += (strlen(url) + 128);
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new): \0211%d\0217\n",malloccount);
    #endif

    strcpy(url_buffer, url);
  }

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;
      strcpy(erb.errmess, "Used same buffer in fetchpage_new!");
      show_error_ret(&erb);
    }

  #endif

  urlutils_fix_url(url_buffer, strlen(url_buffer) + 128);

  return fetchpage_preprocessed(b, record, stop);
}

/*************************************************/
/* fetchpage_new_add()                           */
/*                                               */
/* As fetchpage_new, but takes a second string,  */
/* which is data to be concatenated onto the end */
/* of the given URL. This may be useful for      */
/* image maps or forms data. You may also        */
/* specify whether this URL is to be fetched in  */
/* a new browser window or not.                  */
/*                                               */
/* Restrictions as for fetchpage_new.            */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to which the new URL refers;      */
/*                                               */
/*             Pointer to the new URL string;    */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0;         */
/*                                               */
/*             1 to stop other page fetches in   */
/*             any other frames related to this  */
/*             document, 0 to only stop those in */
/*             this frame;                       */
/*                                               */
/*             Pointer to the data to add onto   */
/*             the end of the URL string;        */
/*                                               */
/*             1 to fetch the URL in a new       */
/*             window, else 0;                   */
/*                                               */
/*             If opening in a new window,       */
/*             pointer to the window name (if    */
/*             wanted), or NULL.                 */
/*************************************************/

_kernel_oserror * fetchpage_new_add(browser_data * b, const char * url, int record, int stop, const char * add, int new_window, const char * name)
{
  /* Don't proceed unless there's something to fetch */

  if (!url) return fetch_cancel(b);

  /* The URL may have been passed in from the 'tokens' buffer, */
  /* and fetch cancels etc. might corrupt it. So take a copy   */
  /* of it before proceeding further, if the URL didn't come   */
  /* from this buffer already.                                 */

  if (url != url_buffer)
  {
    if (url_buffer)
    {
      #ifdef TRACE
        malloccount -= strlen(url_buffer) + 128;
        if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new_add): \0212%d\0217\n",malloccount);
      #endif

      free(url_buffer);
    }

    url_buffer = malloc(strlen(url) + strlen(add) + 128);

    if (!url_buffer) return make_no_fetch_memory_error(7);

    #ifdef TRACE
      malloccount += (strlen(url) + strlen(add) + 128);
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new_add): \0211%d\0217\n",malloccount);
    #endif

    strcpy(url_buffer, url);
    strcat(url_buffer, add);
  }

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;
      strcpy(erb.errmess, "Used same buffer in fetchpage_new_add!");
      show_error_ret(&erb);
    }

  #endif

  urlutils_fix_url(url_buffer, strlen(url_buffer) + 128);

  if (!new_window || b->full_screen) return fetchpage_preprocessed(b, record, stop);
  else
  {
    RetError(windows_create_browser(url_buffer,
                                    NULL,
                                    NULL,
                                    name,
                                    Windows_CreateBrowser_Normal));

    RetError(browser_inherit(b, last_browser));

    return NULL;
  }
}

/*************************************************/
/* fetchpage_new_raw()                           */
/*                                               */
/* Starts a fetch of a given URL, without doing  */
/* anything to that URL at all except copying it */
/* over to a malloc buffer (to ensure it doesn't */
/* move around, as it would in a flex block).    */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             to which the new URL refers;      */
/*                                               */
/*             Pointer to the new URL string;    */
/*                                               */
/*             1 to record the previous URL in   */
/*             the history list, else 0;         */
/*                                               */
/*             1 to stop other page fetches in   */
/*             any other frames related to this  */
/*             document, 0 to only stop those in */
/*             this frame.                       */
/*************************************************/

_kernel_oserror * fetchpage_new_raw(browser_data * b, const char * url, int record, int stop)
{
  /* Don't proceed unless there's something to fetch */

  if (!url || !(*url)) return fetch_cancel(b);

  /* The URL may have been passed in from the 'tokens' buffer, */
  /* and fetch cancels etc. might corrupt it. So take a copy   */
  /* of it before proceeding further, if the URL didn't come   */
  /* from this buffer already...!                              */

  if (url != url_buffer)
  {
    if (url_buffer)
    {
      #ifdef TRACE
        malloccount -= strlen(url_buffer) + 128;
        if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new_raw): \0212%d\0217\n",malloccount);
      #endif

      free(url_buffer);
    }

    url_buffer = malloc(strlen(url) + 128);

    if (!url_buffer) return make_no_fetch_memory_error(7);

    #ifdef TRACE
      malloccount += (strlen(url) + 128);
      if (tl & (1u<<13)) Printf("** malloccount (fetchpage_new_raw): \0211%d\0217\n",malloccount);
    #endif

    strcpy(url_buffer, url);
  }

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;
      strcpy(erb.errmess, "Used same buffer in fetchpage_new_raw!");
      show_error_ret(&erb);
    }

  #endif

  return fetchpage_preprocessed(b, record, stop);
}

/*************************************************/
/* fetchpage_claim_nulls()                       */
/*                                               */
/* Installs the relevant null event handlers so  */
/* that a fetch may proceed in the Desktop.      */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch.            */
/*************************************************/

void fetchpage_claim_nulls(browser_data * b)
{
  /* Don't register the same handler twice... */

  if (!b->fetch_handler)
  {
    register_null_claimant(Wimp_ENull,(WimpEventHandler *) fetchpage_fetch,b);
    b->fetch_handler = 1;
  }

  /* Animations only apply to an ancestor window, not frames */

  b = utils_ancestor(b);

  /* If the 'drift' handler, to advance the animation to the */
  /* first frame and then stop, is active, remove it as the  */
  /* full-time animation handler is about to take over.      */

  if (b->anim_drift)
  {
    deregister_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation_drift,b);
    b->anim_drift = 0;
  }

  /* Register the full time animation handler */

  if (!b->anim_handler && !b->plugin_active)
  {
    /* Is there an appropriate gadget in the status bar? */

    ObjectId lower = toolbars_get_lower(b);
    BBox     box;

    if (!gadget_get_bbox(0, lower, StatusBarAnimAnim, &box))
    {
      /* Yes, so install an animation handler. */

      register_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation,b);
      b->anim_handler = 1;
    }
  }

  /* Update the button bar */

  toolbars_set_button_states(b);

  /* Record the usage of the animation handler. This will increment */
  /* once in the ancestor object for every child fetch, so that     */
  /* the animation handler can finally be released when all the     */
  /* child fetches have stopped.                                    */

  b->current_fetches++;
}

/*************************************************/
/* fetchpage_release_nulls()                     */
/*                                               */
/* Releases all relevant null event handlers     */
/* used for a fetch in the Desktop, *EXCEPT* for */
/* an animation drift handler, which, if the     */
/* Controls dictate it, will be installed.       */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the fetch.            */
/*************************************************/

void fetchpage_release_nulls(browser_data * b)
{
  /* Don't register the same handler twice... */

  if (b->fetch_handler)
  {
    deregister_null_claimant(Wimp_ENull,(WimpEventHandler *) fetchpage_fetch,b);
    b->fetch_handler = 0;
  }

  /* Animations only apply to an ancestor window, not frames */

  b = utils_ancestor(b);

  /* Only remove the handlers if there are no fetches in any */
  /* children, etc. (see fetchpage_claim_nulls comments).    */

  b->current_fetches--;

  if (!b->current_fetches)
  {
    /* Deregister the full time animation handler */

    if (b->anim_handler && !b->plugin_active)
    {
      deregister_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation,b);
      b->anim_handler = 0;
    }

    /* If the Controls say to install the 'drift' handler to ensure   */
    /* the animation finishes on the first frame, and that handler is */
    /* not already installed, install it.                             */

    if (controls.anim_drift && !b->anim_drift && !b->plugin_active)
    {
      register_null_claimant(Wimp_ENull,(WimpEventHandler *) toolbars_animation_drift,b);
      b->anim_drift = 1;
    }
  }

  /* Update the button bar */

  toolbars_set_button_states(b);
}