/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (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.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "pa_parse.h" #include #include "merrors.h" #include "net.h" #include "hk_funcs.h" #include "libevent.h" #include "intl_csi.h" extern int MK_OUT_OF_MEMORY; #ifdef PROFILE #pragma profile on #endif #ifdef XP_WIN16 #define HOLD_BUF_UNIT 32000 #define SIZE_LIMIT 32000 #else #define HOLD_BUF_UNIT 16384 #endif /* XP_WIN16 */ #define WRITE_READY_SIZE (unsigned int) 8192 /* * Function to call with parsed tag elements. * It should be initialized by a call to PA_ParserInit*(). */ static intn (*PA_ParsedTag)(void *data_object, PA_Tag *tags, intn status) = NULL; typedef struct pa_DocDataList_struct { MWContext* window_id; pa_DocData *doc_data; struct pa_DocDataList_struct *next; } pa_DocDataList; static pa_DocDataList *DocDataList = NULL; static pa_DocData *pa_FetchDocData(MWContext *window_id); static Bool pa_RemoveDocData(pa_DocData *target_doc_data); static Bool pa_StoreDocData(MWContext *window_id, pa_DocData *new_doc_data); /************************* * The following is to speed up case conversion * to allow faster checking of caseless equal among strings. *************************/ #ifndef NON_ASCII_STRINGS unsigned char lower_lookup[256]={ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50, 51,52,53,54,55,56,57,58,59,60,61,62,63,64, 97,98,99,100,101,102,103,104,105,106,107,108,109, 110,111,112,113,114,115,116,117,118,119,120,121,122, 91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110, 111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146, 147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164, 165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182, 183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, 201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218, 219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236, 237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254, 255}; #endif /* not NON_ASCII_STRINGS */ /************************************* * Function: pa_caseless_equal * * Description: This function will compare two * strings, similar to strcmp(), but ignoring the * case of the letters A-Z. * * Params: Takes two \0 terminated strings. * * Returns: 1 if strings are equal, 0 if not. *************************************/ #if 0 static intn pa_caseless_equal(char *string_1, char *string_2) { /* * If either is NULL, they are not equal, even if both are NULL */ if ((string_1 == NULL)||(string_2 == NULL)) { return(0); } /* * While not at the end of the string, if they ever differ * they are not equal. */ while ((*string_1 != '\0')&&(*string_2 != '\0')) { if (TOLOWER((unsigned char) *string_1) != TOLOWER((unsigned char) *string_2)) { return(0); } string_1++; string_2++; } /* * One of the strings has ended, if they are both ended, then they * are equal, otherwise not. */ if ((*string_1 == '\0')&&(*string_2 == '\0')) { return(1); } else { return(0); } } #endif /************************************* * Function: pa_TagEqual * * Description: This function is a special purpose caseless compare * to save me a few cycles of performance. * Since we know the first string is a predefined TAG * we are guaranteeing it will always be in lower case, * thus we don't need to TOLOWER its characters as we * compare them. * * Params: Takes two \0 terminated strings. The first, being a predefined TAG * is guaranteed to be all in lower case. * * Returns: 1 if strings are equal, 0 if not. *************************************/ intn pa_TagEqual(char *tag, char *str) { /* * If str is NULL, they are not equal, tag cannot be NULL. */ if (str == NULL) { return(0); } /* * While not at the end of the string, if they ever differ * they are not equal. */ while ((*tag != '\0')&&(*str != '\0')) { if ((int)(*tag) != TOLOWER((unsigned char) *str)) { return(0); } tag++; str++; } /* * One of the strings has ended, if they are both ended, then they * are equal, otherwise not. */ if ((*tag == '\0')&&(*str == '\0')) { return(1); } else { return(0); } } /************************************* * Function: PA_FreeTag * * Description: This function frees up all memory associated * with a PA_Tag structure, including the structure * itself. * * Params: Takes pointer to a PA_Tag structure. * * Returns: none. *************************************/ void PA_FreeTag(PA_Tag *tag) { /* * Nothing to do for already freed tags. */ if (tag == NULL) { return; } /* * If we have data, free it. */ if (tag->data != NULL) { PA_FREE(tag->data); } /* * Free the tag structure. */ XP_DELETE(tag); } static int32 doc_id_gen = 0; /* generator for document identifiers */ /************************************* * Function: pa_new_document * * Description: register a new document, create and initialize * the pa_DocData structure for it. * * Params: Takes a unique document id, and the URL_Struct for this doc. * * Returns: a pointer to the new pa_DocData structure, already * initialized, and with the doc_id filled in. * Return NULL on failure. *************************************/ static pa_DocData * pa_new_document(FO_Present_Types format_out, MWContext *window_id, PA_OutputFunction *output_func, URL_Struct *url_struct) { pa_DocData *doc_data, *old_doc_data; Bool is_inline_stream; if (format_out == FO_PRESENT_INLINE) { is_inline_stream = TRUE; old_doc_data = pa_FetchDocData(window_id); } else { /* Added by Lou: * This will interrupt anything else trying to go into * this same window, so that we have a clear path * to load this new document */ NET_SilentInterruptWindow(window_id); is_inline_stream = FALSE; } doc_data = XP_NEW(pa_DocData); if (doc_data == NULL) { return(NULL); } /* * Allocate a static hold buffer. This will * save on malloc calls in the long run. */ doc_data->hold_buf = XP_ALLOC_BLOCK(HOLD_BUF_UNIT * sizeof(char)); if (doc_data->hold_buf == NULL) { XP_DELETE(doc_data); return(NULL); } /* * Now that we can't fail, create the unique document ID. */ if (is_inline_stream) { /* * If we're still parsing the original HTML stream, then * just get some of the required information from the * existing doc_data. Otherwise, we need to get it from * MWContext - is there a better way to do this? */ if (old_doc_data) { doc_data->doc_id = old_doc_data->doc_id; doc_data->layout_state = old_doc_data->layout_state; } else { doc_data->doc_id = window_id->doc_id; doc_data->layout_state = NULL; } } else { doc_data->doc_id = ++doc_id_gen; doc_data->layout_state = NULL; } doc_data->window_id = window_id; doc_data->output_tag = output_func; doc_data->hold = 0; doc_data->hold_size = HOLD_BUF_UNIT; doc_data->hold_len = 0; doc_data->brute_tag = P_UNKNOWN; doc_data->comment_bytes = 0; doc_data->lose_newline = FALSE; if (url_struct->address == NULL) { doc_data->url = NULL; } else { doc_data->url = XP_STRDUP(url_struct->address); } #ifdef NU_CACHE /* TODO -Gagan*/ doc_data->from_net = (url_struct->cache_file != NULL) ? FALSE : TRUE; #else if ((url_struct->cache_file != NULL)||(url_struct->memory_copy != NULL)) { doc_data->from_net = FALSE; } else { doc_data->from_net = TRUE; } #endif /* NU_CACHE */ /* * A NET_SUPER_RELOAD should always make everything reload, so no * matter what, act as if it all came new from the net. */ if (url_struct->force_reload == NET_SUPER_RELOAD) { doc_data->from_net = TRUE; } /* save the url struct and make sure it doesn't disappear on us */ doc_data->url_struct = url_struct; NET_HoldURLStruct(doc_data->url_struct); doc_data->edit_buffer = NULL; doc_data->is_inline_stream = is_inline_stream; if (!doc_data->is_inline_stream || !old_doc_data) pa_StoreDocData(window_id, doc_data); doc_data->overflow_stack = 0; doc_data->overflow_depth = 0; doc_data->stream_status = 0; doc_data->stream_count = 0; return(doc_data); } /* * */ unsigned int pa_ParseWriteReady (NET_StreamClass *stream) { pa_DocData *doc_data = (pa_DocData *) stream->data_object; #if !defined(XP_UNIX) && !defined(XP_WIN32) if (doc_data->overflow_depth) return 0; #endif return WRITE_READY_SIZE; } static pa_DocData * pa_check_doc_data_count(NET_StreamClass *stream) { pa_DocData *doc_data=stream->data_object; /* netlib will no longer send us data */ if (doc_data->stream_count + doc_data->overflow_depth <= 0) { if (doc_data->stream_status != 0) { PA_MDLAbort(stream, doc_data->stream_status); } else { PA_MDLComplete(stream); } return NULL; } return doc_data; } /* * Flush the data out of the current overflow buffer and send it up through * the parser. After we push the data through PA_ParseBlock() * the overflow buffer is empty, any unparsed data will be left * in the hold buffer */ void pa_FlushOverflow(NET_StreamClass *stream) { pa_DocData *doc_data=stream->data_object; int len = 0; pa_Overflow *overflow; char *buf = NULL; if (!doc_data || doc_data->overflow_depth) return; /* * Make sure the doc_data doesn't get removed out from under us. */ PA_HoldDocData(doc_data); /* ** We need to coalesce the overflow buffers into a single one, since ** we don't want to be calling PA_ParseBlock multiple times -- as this could ** possibly get us back into the overflow code. */ for (overflow = doc_data->overflow_stack; overflow != NULL; overflow = overflow->next) len += overflow->len; if (len > 0) { buf = (char *)XP_ALLOC_BLOCK((len + 1) * sizeof(char)); buf[0] = 0; } while ((overflow = PA_PopOverflow(doc_data))) { if (buf != NULL) XP_STRNCAT(buf, (char*)overflow->buf, overflow->len); PA_FreeOverflow(overflow); } doc_data->overflow_stack = NULL; if (buf != NULL) { PA_ParseBlock(stream, (const char *) buf, len); XP_FREE(buf); } PA_DropDocData(stream); } /* * */ static void pa_unload_complete(NET_StreamClass *stream) { pa_DocData *doc_data = (pa_DocData *) stream->data_object; /* The overflow value was set to one just before sending the JS onUnload event and the only thing that can change it is either a document.write or encountering a