webengine/wmlengine/src/wml/src/WMLUrlNavigation.cpp
author Andy Simpson<andrews@symbian.org>
Mon, 08 Jun 2009 16:03:07 +0100
changeset 3 4e8ca49e7f33
parent 0 dd21522fd290
permissions -rw-r--r--
Tag source matching PDK release 2.0.b

/*
* Copyright (c) 1999 - 2001 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


/*
    $Workfile: wml_url_navigation.c $

    Purpose:

        Class: WmlBrowser

        Browser url navigation logic

*/
#include "nwx_defs.h"
#include "nwx_memseg.h"
#include "nwx_string.h"
#include "nwx_url_utils.h"
#include "wml_elm_attr.h"

#include "wml_task.h"
#include "wml_api.h"
#include "wml_history.h"
#include "wml_url_utils.h"
#include "wml_decoder.h"

#include "nw_hed_documentroot.h"
#include "mvcshell.h"
#include "nw_lmgr_rootbox.h"
#include "nw_wml1x_wml1xcontenthandler.h"
#include "BrsrStatusCodes.h"


static TBrowserStatusCode LoadDeckError(NW_Wml_t* thisObj, NW_DeckDecoder_t *tmp_decoder,
                                  NW_DeckDecoder_t *old_decoder, NW_Ucs2* url);

static TBrowserStatusCode SetTimerKey(NW_Wml_t *wml);
static TBrowserStatusCode GetMethod(NW_Wml_t *thisObj, NW_Wml_Element_t *go_elem, NW_Bool *method_is_post);
static TBrowserStatusCode MakeReqUrl(NW_Wml_t *thisObj, NW_Ucs2 *card_name, NW_Ucs2 **patchedReqUrl);
static TBrowserStatusCode MoveToCard(NW_Wml_t *thisObj, NW_Ucs2 *card_name);
static TBrowserStatusCode ProcessIntraDeckGoTask(NW_Wml_t *thisObj, NW_Ucs2 *reqUrl, NW_Bool updateHistory);
static TBrowserStatusCode ProcessIntraDeckFetchUrlTask(NW_Wml_t *thisObj, NW_Ucs2 *reqUrl, NW_Bool updateHistory);
static TBrowserStatusCode MustReload(NW_Wml_t *thisObj, NW_Wml_Element_t *go_elem, NW_Bool *reload);
static TBrowserStatusCode GetImageUrl(NW_Wml_t *that, NW_Uint16 imgel_id, NW_Wml_Attr_t image_attr,
                                                       NW_Ucs2 **ret_string);

/*------------------------------------------------------------
 * SetTimerKey 
 * If a timer is running, set the TIMER's KEY variable to the
 * remaining time.
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *----------------------------------------------------------*/  

static TBrowserStatusCode SetTimerKey(NW_Wml_t *thisObj)
{
  TBrowserStatusCode status;
  NW_Uint32   timer_value = 0;
  NW_Bool     running = NW_FALSE;
  
  NW_ASSERT(thisObj != NULL);

  status = (TIMER_API->isRunning)(thisObj->browser_app,&running);
  if (status != KBrsrSuccess || !running)
    return status;
  
  if ((status = NW_Wml_SetTimerKey(thisObj, WAE_TIMER_FROM_TIMER, &timer_value))
      == KBrsrOutOfMemory)
    return status;
  thisObj->script_state.timer_time = timer_value;

  if ((status = (TIMER_API->stop)(thisObj->browser_app)) != KBrsrSuccess)
    return status;
  (void)(TIMER_API->destroy)(thisObj->browser_app);

  return status;
}

/*---------------------------------------------------------------------------
 * GetMethod 
 *
 * Sets method_is_post true if the method really, truly is 'post'
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *-------------------------------------------------------------------------*/
static TBrowserStatusCode GetMethod(NW_Wml_t *thisObj, 
                             NW_Wml_Element_t *go_elem, 
                             NW_Bool  *method_is_post)
{
  NW_ASSERT(thisObj != NULL);

  *method_is_post = NW_FALSE;

  if (go_elem == NULL)
    return KBrsrSuccess;

  *method_is_post = NW_DeckDecoder_HasAttributeWithValue(thisObj->decoder, go_elem, METHOD_ATTR, 
      WAE_ASC_POST_STR, thisObj->var_list);

  return KBrsrSuccess;
}

/*---------------------------------------------------------------------------
 * GetCacheControl 
 *
 * Sets noCache true if the cache-control attrib. value is 'no-cache'
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *-------------------------------------------------------------------------*/
static TBrowserStatusCode GetCacheControl(NW_Wml_t         *thisObj, 
                                          NW_Wml_Element_t *go_elem, 
                                          NW_Bool          *noCache)
{
  NW_ASSERT(thisObj != NULL);

  *noCache = NW_FALSE;

  if (go_elem == NULL)
    return KBrsrSuccess;

  *noCache = NW_DeckDecoder_HasAttributeWithValue(thisObj->decoder, go_elem, CACHECONTROL_ATTR, 
      WAE_ASC_NOCACHE_STR, thisObj->var_list);

  return KBrsrSuccess;
}

/*---------------------------------------------------------------------------
 * GetPostfields 
 *
 * Sets postfields to true if a postfields elmement is within the go tag
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *-------------------------------------------------------------------------*/
static TBrowserStatusCode GetPostfields(NW_Wml_t         *thisObj, 
                                        NW_Wml_Element_t *go_elem, 
                                        NW_Bool          *isPostField)
{
  TBrowserStatusCode status = KBrsrFailure;
  NW_NVPair_t *postfields     = NULL;

  NW_ASSERT(thisObj != NULL);

  *isPostField = NW_FALSE;

  if (go_elem == NULL)
  {
    return KBrsrSuccess;
  }

  status = NW_DeckDecoder_GetPostfieldData(thisObj->decoder,
              thisObj->var_list, go_elem, &postfields);

  if (postfields != NULL) 
    {
      *isPostField = NW_TRUE;
      NW_NVPair_Delete( postfields );
      postfields = NULL;
   }

  return status;
}

/*---------------------------------------------------------------------
 * MustReload
 *
 * We must reload if there are any postfields, or the method of the
 * go element is 'post'.
 *
 * returns:
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *---------------------------------------------------------------------*/ 
static TBrowserStatusCode MustReload(NW_Wml_t *thisObj, 
                                     NW_Wml_Element_t *go_elem, 
                                     NW_Bool  *reload)
{
  NW_Bool     isPost;
  NW_Bool     noCache;
  NW_Bool     isPostField;

  NW_ASSERT(thisObj != NULL);
  NW_ASSERT(reload != NULL);

  *reload = NW_FALSE;

  GetPostfields(thisObj, go_elem, &isPostField);
  
  GetMethod(thisObj, go_elem, &isPost);

  GetCacheControl(thisObj, go_elem, &noCache);

  *reload = (NW_Bool) (noCache || isPost || isPostField);
  return KBrsrSuccess;
}

/*----------------------------------------------------------------------------------
 * MakeReqUrl
 *
 * Workaround for PDC Gateway issue -- we remanufacture the full destination from 
 * the history request url, not from the response url, for an intradeck 'go'
 *
 * returns KBrsrOutOfMemory
 *         KBrsrMalformedUrl
 *         KBrsrSuccess
 *---------------------------------------------------------------------------------*/
static TBrowserStatusCode MakeReqUrl(NW_Wml_t *thisObj, 
                              NW_Ucs2  *card_name,
                              NW_Ucs2 **patchedUrlReq)
{
  TBrowserStatusCode status;
  NW_Ucs2     *url;

  NW_ASSERT(thisObj != NULL);
  NW_ASSERT(patchedUrlReq != NULL);


  status = NW_Wml_HistoryGetRequestUrl(thisObj, &url);
  if (status == KBrsrOutOfMemory)
    return status;

  status = NW_Url_BuildAbsUrl(url, card_name, patchedUrlReq);
  NW_Str_Delete(url);

  return status;
}

/*----------------------------------------------------------------------
 * MoveToCard
 *
 * Positions the deck decoder to the given card, if possible. If not,
 * then the decoder is positioned to the first card in the deck,
 * per WML 1.2, section 12.5.1#4c.
 *
 * Note -- this works ok even if the card_name parameter is NULL.
 *
 * returns
 *    KBrsrSuccess
 *    KBrsrOutOfMemory
 *---------------------------------------------------------------------*/   
static TBrowserStatusCode MoveToCard(NW_Wml_t *thisObj, NW_Ucs2 *card_name)
{
  TBrowserStatusCode status = KBrsrSuccess; 

  NW_ASSERT(thisObj != NULL);
  
  if(thisObj->decoder != NULL)
    status = NW_DeckDecoder_SetCardByName(thisObj->decoder, card_name);
  
  if (status == KBrsrOutOfMemory)
      return status;  

  /* wrong card_name */
  if (status == KBrsrWmlbrowserCardNotInDeck) {
    /* Try using a NULL card_name to enforce WML 12.5.1#4c */
    status = NW_DeckDecoder_SetCardByName(thisObj->decoder, NULL);
  }
  return status;
}

/*--------------------------------------------------------------------------
 * ProcessIntraDeckGoTask
 *
 * NOTE -- the 'GO_TASK' case contains a workaround for a PDC gateway
 * issue.  Specifically the problem is this -- the PDC gateway sometimes
 * sends back a ContentLocation header indicating that the gateway (e.g.,
 * 'krell.uplanet.com') is the location of the redirected content.
 * Since we derive the 'response_url' from the ContentLocation header,
 * and we resolve relative references with respect to the response url,
 * we have a problem.
 *
 * So, in the case of an intradeck 'go', we need to manufacture a 
 * request URL that contains the real (originally requested)
 * deck URL, plus the _requested_ card name (which is part of reqUrl).
 * Then we need to manufacture a response URL that consists of the
 * request URL plus the _actual_ card that we are going to display (this
 * may or may not be the requested card, and it may or may not have a name).
 *
 * Suppose, for example, the current deck was requested as 
 * "http://foo/deck.wml#card1", and the response_url (derived from
 * the ContentLocation header) from the PDC gateway is 
 * "http://foo/krell.uplanet.com#card1".  Suppose further that the next
 * request from that card is to go to "#card2", but there isn't a 
 * 'card2', and that 'main' is the id of the first card in the deck.
 *
 * Then we need to manufacture a request url of "http://foo/deck.wml#card2"
 * and a response url of "http://foo/krell.uplanet.com#main".
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *-------------------------------------------------------------------------*/

static TBrowserStatusCode ProcessIntraDeckGoTask(NW_Wml_t *thisObj, 
                                          NW_Ucs2 *url, 
                                          NW_Bool updateHistory)
{
  NW_Ucs2     *cardName    = NULL;
  NW_Ucs2     *responseUrl = NULL;
  NW_Ucs2     *requestUrl  = NULL;
  TBrowserStatusCode status;

  NW_ASSERT(thisObj != NULL);
  NW_ASSERT(url != NULL);

  status = NW_DeckDecoder_GetCardName(thisObj->decoder, &cardName);
  if (status != KBrsrSuccess && status != KBrsrFailure)
    goto Exit;

  status = NW_Url_BuildAbsUrl(url,cardName,&responseUrl);
  if (status != KBrsrSuccess)
    goto Exit;

  status = MakeReqUrl(thisObj,NW_Url_Fragment(url),&requestUrl);
  if (status != KBrsrSuccess)
    goto Exit;

  /* now create and push a new History entry */
  if (updateHistory == NW_TRUE) {
    status = NW_Wml_HistoryPushIntraDeckLoad(thisObj, requestUrl, responseUrl);
    if (status != KBrsrSuccess && status != KBrsrFailure)
      goto Exit;
  }

  status = NW_Wml_ProcessGo(thisObj,responseUrl);
  if (status != KBrsrSuccess && status != KBrsrFailure)
    goto Exit;      /* FAILURE does not imply a fatal error */
  status = KBrsrSuccess;

Exit:
  NW_Str_Delete(cardName);
  NW_Str_Delete(requestUrl);
  NW_Str_Delete(responseUrl);
  return status;
}

/*-----------------------------------------------------------------------
 * ProcessIntraDeckFetchUrlTask
 *
 * Handles intradeck navigation tasks as indicated by the the 'curr_task'
 * member of the NW_Wml_t object.
 *
 * returns
 *  KBrsrSuccess
 *  anything else is an unrecoverable error
 *---------------------------------------------------------------------*/
static TBrowserStatusCode ProcessIntraDeckFetchUrlTask(NW_Wml_t *thisObj,
                                                NW_Ucs2  *reqUrl, 
                                                NW_Bool updateHistory)
{
  TBrowserStatusCode status;
  NW_Wml1x_ContentHandler_t* thisHandlerObj;

  NW_ASSERT(thisObj != NULL);

  status = NW_Wml_CancelAllLoads(thisObj);
	thisHandlerObj = NW_Wml1x_ContentHandlerOf(thisObj->browser_app);

  switch (thisObj->curr_task)
  {
    case GO_TASK:
      NW_ASSERT(reqUrl != NULL);
      status = ProcessIntraDeckGoTask(thisObj, reqUrl, updateHistory);
      break;

    case PREV_TASK:
      NW_ASSERT(reqUrl != NULL);
      status = NW_Wml_ProcessPrev(thisObj, reqUrl);
    
      if (status != KBrsrSuccess && status != KBrsrFailure)
        break;    /* FAILURE does not imply a fatal error */
      status = KBrsrSuccess;
      break;

    case REFRESH_TASK:
      status = NW_Wml_ProcessRefresh(thisObj);
      
      if (status != KBrsrSuccess && status != KBrsrFailure)
        break;   /* FAILURE does not imply a fatal error */
      status = KBrsrSuccess;
      break;

    case RESTORE_TASK:
      /* TODO do we need to do more to restore/restart a card */
			if(thisHandlerObj->contextSuspended)
			{
				/* apply the global variables to the wml interpreter */
				NW_DeckDecoder_ContextToVarList (thisHandlerObj->contextSuspended, thisHandlerObj->wmlInterpreter.var_list,
						thisHandlerObj->wmlInterpreter.mem_segment_general);
				/* clear the global variable context */
				NW_Object_Delete (thisHandlerObj->contextSuspended);
				thisHandlerObj->contextSuspended = NULL;
			}
      NW_Wml_DisplayCard(thisObj, NW_FALSE);
      status = NW_Wml_HandleTimer(thisObj);

      status = KBrsrSuccess;
      break;
  
    case INVALID_TASK:
    case NOOP_TASK:
    default:
      status = KBrsrFailure;
  }

  return status;
}


static TBrowserStatusCode NW_Wml_HandleIntraPageNavigationStarted (NW_Wml_t *thisObj)
{
  NW_HED_DocumentRoot_t* docRoot;

  docRoot = (NW_HED_DocumentRoot_t*) NW_HED_DocumentNode_GetRootNode (thisObj->browser_app);

  thisObj->outstandingLoadCount = 0;

  NW_LMgr_RootBox_t* rootBox = NW_HED_DocumentRoot_GetRootBox(docRoot);
  (void) NW_ADT_DynamicVector_Clear(rootBox->iOutOfViewListeners);

  return NW_HED_DocumentRoot_HandleIntraPageNavigationStarted (docRoot);
}


static TBrowserStatusCode NW_Wml_HandleIntraPageNavigationCompleted (NW_Wml_t *thisObj)
{
  NW_HED_DocumentRoot_t* docRoot;

  docRoot = (NW_HED_DocumentRoot_t*) NW_HED_DocumentNode_GetRootNode (thisObj->browser_app);

  if (thisObj->IntraDeck_OefCounter > MAX_NUMBER_INTRA_OEF) {
    /* we have exceeded the maximunm # of OnEnterForwards allowable for the deck 
       The error will be handled after we process the deck - Vishy05/14/2002
    */
    docRoot->isIntraPageNav--;

    return KBrsrSuccess;
  }
  else {
    return NW_HED_DocumentRoot_HandleIntraPageNavigationCompleted (docRoot);
  }
}

TBrowserStatusCode NW_Wml_HandleIntraDocRequest (NW_Wml_t *thisObj, 
                                          NW_Wml_Task_e task,
                                          NW_Bool historicRequest,
                                          const NW_Ucs2 *url)
{
  TBrowserStatusCode status = KBrsrWmlbrowserCardNotInDeck;
  NW_Bool mustCallCompleted = NW_FALSE;
  NW_Bool cardNotFound = NW_FALSE;
  NW_Wml1x_ContentHandler_t* wmlCH;

  /* set the wml-task */
  thisObj->curr_task = task;

  if(task != RESTORE_TASK)
  {
    wmlCH = NW_Wml1x_ContentHandlerOf(thisObj->browser_app);
	NW_Object_Delete (wmlCH->contextSuspended);
	/* copy the suspended context */
	wmlCH->contextSuspended = NW_DeckDecoder_VarListToContext(wmlCH->wmlInterpreter.var_list);
  }

  /* notify the docRoot that we are performing an intra-document navigation */
  NW_Wml_HandleIntraPageNavigationStarted (thisObj);
  mustCallCompleted = NW_TRUE;

  /* set the card.  The url will be NULL when we are "restoring" a card */
  if (url != NULL) {
    NW_String_t cardName;

    /* if the intra-nav dest card is not found, it is not a fatal error */
    /* see WML-191, section 12.5.1.4 */

    NW_String_initialize (&cardName, NW_Url_Fragment((NW_Ucs2 *) url), HTTP_iso_10646_ucs_2);
    
    if (cardName.storage != NULL) {
      if (!NW_Wml_Deck_GetCardByName (thisObj->decoder->domDeck, &cardName))
      {
        cardNotFound = NW_TRUE;
      }
    }

    /* set the current card */
    NW_THROWIF_ERROR (status = MoveToCard(thisObj, NW_Url_Fragment((NW_Ucs2 *) url)));
  }

  /* show the intra-deck card */
  NW_THROWIF_ERROR (status = ProcessIntraDeckFetchUrlTask(thisObj, (NW_Ucs2 *) url, 
      (NW_Bool) (historicRequest == NW_FALSE)));

  /* notify the docRoot that we are done navigating */
  NW_Wml_HandleIntraPageNavigationCompleted (thisObj);

  if (cardNotFound == NW_TRUE) /* if the requested card was not found - WML-191, 12.5.1.4 is followed, */
  {                            /* and notify user that a nav exception has occured, to let them know. */
    NW_Wml_HandleError (thisObj, BRSR_STAT_CLASS_GENERAL, (NW_Int16) KBrsrWmlbrowserCardNotInDeck);
  }

  /* When we restore a previous document we do not want to start the timers again! - Vishy 07/02/2002 */
  if(task == RESTORE_TASK) {    
    wmlCH = NW_Wml1x_ContentHandlerOf(thisObj->browser_app);
    (void) (wmlCH->wmlInterpreter).wml_api->timer->destroy(wmlCH);
    wmlCH->allowTimer = NW_FALSE;
  }

  /* update the content handler to point to the frag's url-request */
  {
    NW_HED_DocumentNode_t* docNode = NULL;
    NW_HED_DocumentRoot_t* docRoot = NULL;
    NW_HED_HistoryStack_t* history = NULL;
    NW_HED_HistoryEntry_t* entry = NULL;

    docNode = NW_HED_DocumentNodeOf (thisObj->browser_app);
    docRoot =
      (NW_HED_DocumentRoot_t*) NW_HED_DocumentNode_GetRootNode (docNode);
    history = NW_HED_DocumentRoot_GetHistoryStack (docRoot);

    entry = NW_HED_HistoryStack_GetEntry (history, NW_HED_HistoryStack_Direction_Current);
    
    status = NW_HED_ContentHandler_SetAssociatedHistoryEntry (docNode, entry);
  }

  return status;

NW_CATCH_ERROR
  /*lint -e{774} Significant prototype coercion*/
  if (mustCallCompleted == NW_TRUE) {
    NW_Wml_HandleError (thisObj, BRSR_STAT_CLASS_GENERAL, (NW_Int16) status);
	//We need to set the IntraDeck_OefCounter to 0 so that one card is shown for the infinite loop and also preventing a crash
	//down the road. The bad content error message will be shown through the next function call.
	thisObj->IntraDeck_OefCounter = 0;
    NW_Wml_HandleIntraPageNavigationCompleted (thisObj);

    if (thisObj->IntraDeck_OefCounter > MAX_NUMBER_INTRA_OEF) {

      /* this is a cronic error and we want to propagate this error to the 
        to the top - Vishy 05/14/2002*/
      status  = KBrsrWmlbrowserBadContent; 
    }
    else {
      /* the error is handled above so set status to success */
      status = KBrsrSuccess;
    }
  }

  return status;
}

/*--------------------------------------------------------------------------- 
 * NW_Wml_FetchUrlGeneric
 *
 * This is the same as NW_Wml_FetchUrlGeneric, but allows for more flexibility.
 * It does not rely on a GO element to provide the semantics of the load,
 * but receives these directly in the argument list.
 *
 * RETURN: KBrsrSuccess
 *         NW_FAILURE - does NOT imply a fatal error 
 *                    - card does not have an intrinsic event
 *         KBrsrBadInputParam
 *         KBrsrOutOfMemory
 *---------------------------------------------------------------------------*/
TBrowserStatusCode NW_Wml_FetchUrlGeneric(
                            NW_Wml_t *obj, 
                            NW_Ucs2 *url_param,
                            NW_Bool is_method_post,
                            NW_Http_Header_t *header,
                            NW_NVPair_t *postfields,
                            NW_Ucs2 *enctype)
{
  NW_Wml_t    *thisObj       = (NW_Wml_t*) obj;
  TBrowserStatusCode status         = KBrsrFailure;
  NW_Ucs2     *patchedReqUrl = NULL;
  NW_Bool     intraDeck      = NW_FALSE;
  NW_Bool     mustReload     = is_method_post;
  NW_Rfc2396_Parts_t parts   = {0};

  if (!NW_Url_GetRfc2396Parts(url_param,&parts))
    return KBrsrMalformedUrl;

  if ((status = SetTimerKey(thisObj)) != KBrsrSuccess)
    return status;
  
  status = NW_Wml_IsCardInCurrentDeck(thisObj, url_param);
  if (status == KBrsrOutOfMemory)
    return status;

  intraDeck = (NW_Bool) (status == KBrsrSuccess);

  /* 
   * If card is in current deck, then WML Spec 12.5.1.3 and 
   * 12.5.1.4 are satisfied: access control and locating the 
   * destination card
   */

  if (!intraDeck || mustReload) {
    thisObj->curr_task = GO_TASK;
    if (intraDeck) {
      status = MakeReqUrl(thisObj, NW_Url_Fragment(url_param), &patchedReqUrl);
      if (status != KBrsrSuccess)
        return status;
      status = NW_Wml_DoLoadAndSave(thisObj, patchedReqUrl, is_method_post,
                                    NULL, NULL, header,
                                    postfields, enctype, NW_CACHE_NORMAL);
      NW_Str_Delete(patchedReqUrl);
    } else {
      status = NW_Wml_DoLoadAndSave(thisObj, url_param, is_method_post,
                                    NULL, NULL, header,
                                    postfields, enctype, NW_CACHE_NORMAL);
    }
  } 
  
  /* intradeck fetch -- no need to reload the deck */
  else {
    status = NW_Wml_HandleIntraDocRequest (thisObj, thisObj->curr_task, NW_FALSE, url_param);

    if (status != KBrsrSuccess) {
      NW_Wml_ClearTaskVariables(thisObj); /* per WML 12.5.5 */
    }
  }

  return status;
}

/*--------------------------------------------------------------------------- 
 * NW_Wml_FetchUrl
 *
 * Navigate to url_param. url_param is NW_MEM_SEGMENT_MANUAL & not owned by 
 * this routine, but short-lived 
 *
 * NOTE: For an intradeck fetch that doesn't require a reload of the deck,
 * if the curr_task is not reload, we clear thisObj->mem_segment_card and
 * clean the memory segment identified by the 'mem_segment_general' field of the wml 
 * object (this flushes out history strings that were orphaned).
 *
 * RETURN: KBrsrSuccess
 *         NW_FAILURE - does NOT imply a fatal error 
 *                    - card does not have an intrinsic event
 *         KBrsrBadInputParam
 *         KBrsrOutOfMemory
 *---------------------------------------------------------------------------*/
TBrowserStatusCode NW_Wml_FetchUrl(NW_Wml_t *obj, 
                            NW_Ucs2  *url_param, 
                            NW_Wml_Element_t* go_elem)
{
  NW_Wml_t    *thisObj       = (NW_Wml_t*) obj;
  TBrowserStatusCode status         = KBrsrFailure;
  NW_Ucs2     *patchedReqUrl = NULL;
  NW_Bool     intraDeck      = NW_FALSE;
  NW_Bool     mustReload     = NW_FALSE;
  NW_Rfc2396_Parts_t parts   = {0};

  NW_ASSERT(thisObj != NULL);
  NW_ASSERT(url_param != NULL);

  if (NW_Url_GetRfc2396Parts(url_param,&parts) != NW_TRUE)
    return KBrsrMalformedUrl;

  if ((status = SetTimerKey(thisObj)) != KBrsrSuccess)
    return status;
  
  status = NW_Wml_IsCardInCurrentDeck(thisObj, url_param);
  if (status == KBrsrOutOfMemory)
    return status;
  intraDeck = (NW_Bool) (status == KBrsrSuccess);

  if(intraDeck && go_elem)
  {
       NW_Wml_ElType_e el_type;
       //Check if it is <a> tag not the <go> or <anchor> tag. Then let
       //cache decide wheather to keep the same page or load the new one.
       //If we keep the same page then cache parameters are not modified.
       //If there is an <a> tag this is wrong because the cache doesn't implement
       //the wml intradeck naviagation, as it is 12.5.1.3, 12.5.1.4
       NW_DeckDecoder_GetType(thisObj->decoder, go_elem, &el_type);
       if(el_type == ANCHOR_ELEMENT)
       {
        intraDeck = NW_FALSE;
        go_elem   = NULL; //Make same that of the original call.
       }
  }

  if ((status = MustReload(thisObj, go_elem, &mustReload)) != KBrsrSuccess)
    return status;

  /* 
   * If card is in current deck, then WML Spec 12.5.1.3 and 
   * 12.5.1.4 are satisfied: access control and locating the 
   * destination card
   */

  if (!intraDeck || mustReload) {
    thisObj->curr_task = GO_TASK;
    /* reset the counter as we are making an external load */ 
    thisObj->IntraDeck_OefCounter = 0;
    if (intraDeck) {
      status = MakeReqUrl(thisObj, NW_Url_Fragment(url_param), &patchedReqUrl);
      if (status != KBrsrSuccess)
        return status;
      status = NW_Wml_MakeLoadRequest(thisObj, patchedReqUrl, go_elem);
      NW_Str_Delete(patchedReqUrl);
    } else {
      status = NW_Wml_MakeLoadRequest(thisObj, url_param, go_elem);
    }
  } 
  
  /* intradeck fetch -- no need to reload the deck */
  else {
  
  /*Now we need to check if a condition for endless loop of intra-OnEnterForward is met. */
    if(thisObj->IntraDeck_OefCounter > MAX_NUMBER_INTRA_OEF) {
		  /*We stop the navigation to prevent endless loop and 
		    notify the user about the error*/
      return KBrsrWmlbrowserBadContent;
    }
    
    status = NW_Wml_HandleIntraDocRequest (thisObj, thisObj->curr_task, NW_FALSE, url_param);

    if (status != KBrsrSuccess) {
      NW_Wml_ClearTaskVariables(thisObj); /* per WML 12.5.5 */
    }
  }

  return status;
}


/*-------------------------------------------------------------------------
 * NW_Wml_FetchPrevUrl
 *
 * Navigate to the url of the history entry before the current one.
 * 
 * Intradeck fetches cause thisObj->mem_segment_card to be cleared, and
 * cause the wml object's mem_segment_general segment to be cleaned.
 * 
 * RETURN: KBrsrSuccess
 *         KBrsrFailure - history stack is empty
 *         KBrsrOutOfMemory
 *------------------------------------------------------------------------*/
 TBrowserStatusCode NW_Wml_FetchPrevUrl(NW_Wml_t *obj)
 {
  NW_Wml_t         *thisObj = (NW_Wml_t*) obj;
  NW_Wml_History_t *prev_card = NULL;
  TBrowserStatusCode      status = KBrsrSuccess;
  NW_Bool          intraDeck = NW_FALSE;
  
  if ((status = SetTimerKey(thisObj)) != KBrsrSuccess)
    return status;

  if ((status = NW_Wml_HistoryPrevCard(thisObj, &prev_card)) == KBrsrOutOfMemory)
    return status;

  /* 
   * If the history stack is empty, then perhaps a dialog should be 
   * presented to the user.  But the spec does not say this is
   * an error condition.
   */
  if (status == KBrsrFailure)
    goto EndFetchPrev;

  if ((status = NW_Wml_IsCardInCurrentDeck(thisObj, prev_card->req_url))
      == KBrsrOutOfMemory)
    goto EndFetchPrev;
  intraDeck = (NW_Bool)(status == KBrsrSuccess);

  /* The 'pop' of the history stack will occur when NW_Wml_ProcessPrev
     is executed.  Otherwise, we run the risk of popping the history
     stack _before_ the load is completed, and then aborting the load.
     The 'current' url would then be lost!
  */

  /* 
   * if card is in current deck, then WML Spec 12.5.2.3 and 12.5.2.4 are
   * satisfied: access control and locating the destination card
   */
  if (intraDeck)
  {
    NW_Wml_Element_t last_card_el;

    NW_DeckDecoder_GetCardElement (thisObj->decoder, &last_card_el);

    if ((status = MoveToCard(thisObj, NW_Url_Fragment(prev_card->req_url))) 
         == KBrsrOutOfMemory) {
      goto EndFetchPrev;
    }

    if (status == KBrsrSuccess)
    {
      status = NW_Wml_HistoryToPrev(thisObj, NULL);
      if (status != KBrsrSuccess) {
        goto EndFetchPrev;
      }

      /* show the prev card */
      status = NW_Wml_HandleIntraDocRequest (thisObj, PREV_TASK, NW_TRUE, prev_card->req_url);

      if (status != KBrsrSuccess && status != KBrsrFailure)
        /* FAILURE does not imply a fatal error */
        goto EndFetchPrev;
    }
    else
    {
      /* card not found - present dialog app */

      /* do a refresh until UI option list bug is fixed */
      thisObj->curr_task = REFRESH_TASK;
      NW_DeckDecoder_SetCardByElement (thisObj->decoder, &last_card_el);
      status = NW_Wml_DisplayCard(thisObj, NW_TRUE);
      if (status != KBrsrSuccess && status != KBrsrFailure)
        /* FAILURE does not imply a fatal error */
        goto EndFetchPrev;
    }
  }
  else
  {
    thisObj->curr_task = PREV_TASK;
    status = NW_Wml_DoPrevLoad(thisObj->browser_app);
  }

EndFetchPrev:
  NW_Wml_HistoryDeleteEntry(prev_card);

  return status;
}

 
/* Helper function for LoadDeck() to cleanup after errors */
static TBrowserStatusCode LoadDeckError(NW_Wml_t* thisObj, NW_DeckDecoder_t *tmp_decoder,
                          NW_DeckDecoder_t *old_decoder, NW_Ucs2* url)
{

  NW_ASSERT(thisObj != NULL);

  /* Restore old deck decoder */
  if (old_decoder != NULL) {
    thisObj->decoder = old_decoder;
  }

  NW_Str_Delete(url);

  /* Clean up variable state, per WML 12.5.5 */
  NW_Wml_ClearTaskVariables(thisObj);

  NW_Ucs2 *card_name = NULL;
  /* redisplay the old deck only if we have one !*/
  if (thisObj->decoder != NULL && (NW_DeckDecoder_GetCardName(thisObj->decoder, &card_name) != KBrsrSuccess)) {
    return NW_Wml_DisplayCard(thisObj, NW_TRUE);
  }
  else
    {
    return NW_Wml_DisplayCard(thisObj, NULL);
    }
}

/*
 * RETURN KBrsrSuccess - deck is loaded OK
 *        KBrsrFailure - deck is not loaded because of a non-fatal
 *                          error like failing access control
 *        KBrsrOutOfMemory
 *        NW_STAT_*       - propagates fatal return value from CB calls
 */

/*
** TODO this function can be simplified now that we load each deck into their
** own Tempest-content-handler.  Since the wml-content-handler has a unique
** NW_Wml_t structure there will NEVER be previous card or deck to restore to
** if a failure is encountered in this function.  LoadDeckError can also be
** simplified.
*/
TBrowserStatusCode NW_Wml_LoadWmlResp(NW_Wml_t *thisObj, 
                               NW_Buffer_t* responseBuffer,
                               NW_Bool contentWasPlainText,
                               NW_Url_Resp_t* response)
{
  NW_Ucs2             *url         = NULL;
  NW_Ucs2             *card_name   = NULL;
  NW_DeckDecoder_t    *old_decoder = thisObj->decoder;
  NW_DeckDecoder_t    *tmp_decoder = NULL;
  TBrowserStatusCode         status;
  TBrowserStatusCode         error;
  NW_Http_CharSet_t   charset;
  NW_Ucs2             *reqUrl;
  NW_Ucs2             *redirectedUrl;

  NW_ASSERT(thisObj != NULL);

  charset = response->charset;

  reqUrl = NW_Str_Newcpy(response->uri);
  if (reqUrl == NULL) {
    return KBrsrOutOfMemory;
  }

  redirectedUrl = reqUrl;

  /* 
   * check for a redirected url, then free. 
   * both url_param and new_url were malloced without using a mem segment
   */
  url = NW_Wml_CheckRedirection(reqUrl, redirectedUrl);
  if (url == NULL)
  {
    (void) LoadDeckError(thisObj, NULL, NULL, url);
    return KBrsrOutOfMemory;
  }
  NW_ASSERT(url != NULL);

  if (redirectedUrl != reqUrl)
    NW_Mem_Free(redirectedUrl);

  NW_Mem_Free(reqUrl);
  /* create a new DeckDecoder, reuse the resp->context wml bytecode */

  tmp_decoder = NW_DeckDecoder_New();
  if (tmp_decoder == NULL)
  {
    (void) LoadDeckError(thisObj, NULL, NULL, url);
    return KBrsrOutOfMemory;
  }

  status = NW_DeckDecoder_InitializeAndValidate(tmp_decoder, responseBuffer, 
      charset, contentWasPlainText);

  /*
  ** tmp_decoder now owns the content, and will free it when tmp_decoder is
  ** freed, or has already freed it in the case of error.
  */

  if (status != KBrsrSuccess) {
    error = status;
    (void) LoadDeckError(thisObj, tmp_decoder, NULL, url);
    return error;
  }

  /* 
   * Temporarily set thisObj->decoder to tmp_decoder so the access
   * check can be done
   */
  thisObj->decoder = tmp_decoder;

  status = NW_Wml_AccessControl(thisObj, url);
  if (status != KBrsrSuccess) {
    if (status == KBrsrOutOfMemory) {
      error = KBrsrOutOfMemory;
    } else {
      error = KBrsrWmlbrowserNoAccess;
    }
    (void) LoadDeckError(thisObj, tmp_decoder, old_decoder, url);
    return error;
  }

  /* non - WML Spec  */
  /* check for card's meta data and perform custom processing */
  if ((status = NW_Wml_HandleMetaElements(thisObj)) == KBrsrOutOfMemory)
  {
    (void) LoadDeckError(thisObj, tmp_decoder, old_decoder, url);
    return status;
  }

  card_name = NW_Str_Strchr(url, '#');
  if (card_name && NW_Str_StrcmpConst(card_name, WAE_ASC_EMPTY_STR))
    card_name++;
  /* this if branch include card_name = NULL */
  status = NW_DeckDecoder_SetCardByName(thisObj->decoder, card_name);
  if (status != KBrsrSuccess) { 
    /* Try using a NULL card_name to enforce WML 12.5.1#4c */
    status = NW_DeckDecoder_SetCardByName(thisObj->decoder, NULL);
    if (status == KBrsrWmlbrowserCardNotInDeck) {
        (void) LoadDeckError(thisObj, tmp_decoder, old_decoder, url);
        return status;
      }
  }

  /* append the card name to the URL */
  if ((status = NW_DeckDecoder_GetCardName(thisObj->decoder, &card_name))
    == KBrsrSuccess) {
    if(card_name) {
      NW_Ucs2 *new_url = NULL;
      status = NW_Url_BuildAbsUrl(url, card_name, &new_url);
      NW_Str_Delete(card_name);
      if (status != KBrsrSuccess) {
        (void) LoadDeckError(thisObj, tmp_decoder, old_decoder, url);
        return status;
      }
      NW_Str_Delete(url);
      url = new_url;
    }
  }
  /* Card not found is not a fatal error */ 
  if (status == KBrsrOutOfMemory) {
    error = status;
    (void) LoadDeckError(thisObj, tmp_decoder, old_decoder, url);
    return error;
  }

  if ( (thisObj->curr_task == GO_TASK) || (thisObj->curr_task == PREV_TASK) )
  {
    /*
     * Temporarily install the previous decoder so the UI can be notified
     * that the old card can be destroyed 
     */ 
    thisObj->decoder = old_decoder;
    if ((status = (DISPLAY_API->destroyCard)(thisObj->browser_app)) 
        != KBrsrSuccess)
    {
      (void) LoadDeckError(thisObj, tmp_decoder, NULL, url);
      return status;
    }
    /* Restore the new decoder */
    thisObj->decoder = tmp_decoder;
  }

  if (thisObj->curr_task == GO_TASK)
  {
    status = NW_Wml_ProcessGo(thisObj, url);
  }
  else if(thisObj->curr_task == PREV_TASK)
  {
    status = NW_Wml_ProcessPrev(thisObj, url);
  }
  else
  {
    status = KBrsrFailure;
  }
  /* free the old decoder as we have loaded the new deck */
  /* Note: NW_DeckDecoder_Delete() handles NULL pointers */
  NW_DeckDecoder_Delete(old_decoder);

  /* Note: NW_Str_Delete() handles NULL pointers */
  NW_Str_Delete(url);

  if (status != KBrsrSuccess) /* display error and cleanup */
  {
    /* blow up the card here as the user agent might have reported errors
    when displaying the card */
    (void) (DISPLAY_API->destroyCard)(thisObj->browser_app);

    /* Clean up variable state, per WML 12.5.5 */
    NW_Wml_ClearTaskVariables(thisObj);
  }

  if (status != KBrsrSuccess)
    {
    NW_DeckDecoder_Delete(thisObj->decoder);
    thisObj->decoder = NULL;
    }
  return status;
}


/* Retrieve the absolute URL for a image element. This function is called when 
 * only SRC exist. 
 * NOTE: The ret_string parameter has to be freed by the caller by invoking 
 *       the function NW_Wml_FreeString
 * RETURN KBrsrSuccess - URL exits
 *        KBrsrBadInputParam - element not found or bad input param
 *        KBrsrOutOfMemory
 *        NW_STAT_*       - propagates fatal return value from CB calls
 */
TBrowserStatusCode NW_Wml_GetImageUrl(NW_Wml_t *thisObj, NW_Uint16 imgel_id, NW_Ucs2 **ret_string)
{
    NW_Wml_Attr_t image_attr;

  image_attr = SRC_ATTR;
  return (GetImageUrl(thisObj, imgel_id, image_attr, ret_string));
}

/* Retrieve the absolute URL for a image element. This function is called when
 * LOCALSRC attribute exist, it should be loaded first. 
 * NOTE: The ret_string parameter has to be freed by the caller by invoking 
 *       the function NW_Wml_FreeString
 * RETURN KBrsrSuccess - URL exits
 *        KBrsrBadInputParam - element not found or bad input param
 *        KBrsrOutOfMemory
 *        NW_STAT_*       - propagates fatal return value from CB calls
 */

TBrowserStatusCode NW_Wml_GetLocalImageUrl(NW_Wml_t *thisObj, NW_Uint16 imgel_id, NW_Ucs2 **ret_string)
{
  NW_Wml_Attr_t image_attr;

  image_attr = LOCALSRC_ATTR;
  return (GetImageUrl(thisObj, imgel_id, image_attr, ret_string));
}


static TBrowserStatusCode GetImageUrl(NW_Wml_t *thisObj, NW_Uint16 imgel_id, NW_Wml_Attr_t image_attr,
                                                NW_Ucs2 **ret_string)
{
  NW_Int32         id  = -1;
  NW_Wml_Element_t *el =  0;
  NW_Wml_ElType_e  el_type;
  TBrowserStatusCode      status = KBrsrFailure;
  NW_Ucs2          *url   = 0;

  if ((thisObj == NULL) || (id = NW_Wml_FindEl(thisObj, imgel_id)) == -1)
    return KBrsrBadInputParam;

  el = thisObj->card_els[id];
  NW_ASSERT(el != NULL);

  NW_DeckDecoder_GetType(thisObj->decoder, el, &el_type);

  if (el_type == IMAGE_ELEMENT)
  {
    /* get image-attribute */
    if ( (status = NW_DeckDecoder_GetAttribute(thisObj->decoder, el, image_attr,
          thisObj->var_list, NW_MEM_SEGMENT_MANUAL, &url)) == KBrsrOutOfMemory )
    {
      return status;
    }
    
    /* Get url*/
    if (url)
    {
      status = NW_Wml_GetFullUrl(thisObj, url, ret_string);
      NW_Str_Delete(url);
    }
  }
  else
  {
    status = KBrsrBadInputParam;
  }
  return status;
}

TBrowserStatusCode
NW_Wml_CancelAllLoads (NW_Wml_t *thisObj)
{
  NW_HED_DocumentNode_t* rootNode;

  rootNode = NW_HED_DocumentNode_GetRootNode (thisObj->browser_app);
  (void) NW_HED_DocumentNode_Cancel (rootNode, NW_HED_CancelType_NewRequest);

  return KBrsrSuccess;
}

void NW_Wml_HandleError (NW_Wml_t *thisObj, NW_Int16 errorClass, NW_Int16 error)
{
  NW_HED_DocumentNode_t* docNode;
  
  docNode = NW_HED_DocumentNodeOf (thisObj->browser_app);

  if (error == KBrsrWmlbrowserCardNotInDeck)
  {
    NW_HED_DocumentRoot_t* docRoot = NULL;
    
    docRoot =
      (NW_HED_DocumentRoot_t*) NW_HED_DocumentNode_GetRootNode (docNode);
    
    (void) docRoot->appServices->errorApi.
      notifyError (docRoot, (TBrowserStatusCode) error, NULL, NULL);
    return;
  }
  
  NW_HED_DocumentNode_HandleError (docNode, NULL, errorClass, error); 
}