/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "xp.h" #include "net.h" #include "pa_parse.h" #include "layout.h" #include "np.h" #ifdef JAVA #include "java.h" #endif #ifdef ANTHRAX /* 9.23.97 amusil */ #include "prefapi.h" #endif /* ANTHRAX */ /* Internal prototypes */ void lo_FetchObjectData(MWContext*, lo_DocState*, lo_ObjectStack*); void lo_ObjectURLExit(URL_Struct*, int, MWContext*); void lo_ClearObjectBlock(MWContext*, LO_ObjectStruct*); lo_ObjectStack* lo_PushObject(MWContext*, lo_DocState*, PA_Tag*); void lo_PopObject(lo_DocState*); Bool lo_CheckObjectBlockage(MWContext*, lo_DocState*, lo_ObjectStack*); #ifdef ANTHRAX /* 9.23.97 amusil */ static void lo_SetClassID(PA_Tag* tag, char* appletName); static char* lo_GetNextName(char** index); static char* lo_GetNextValue(char** index); static void lo_SetJavaArgs(char* tag, LO_JavaAppStruct* current_java); static void lo_itoa(uint32 n, char* s); static void lo_ReverseString(char* s); #endif /* ANTHRAX */ void lo_FormatObject(MWContext* context, lo_DocState* state, PA_Tag* tag) { lo_TopState* top_state = state->top_state; lo_ObjectStack* top; LO_ObjectStruct* object; PA_Block buff; int16 type = LO_NONE; char* str; #ifdef ANTHRAX XP_Bool javaMimetypeHandler = FALSE; char* appletName; NET_cinfo* fileInfo; #endif /* ANTHRAX */ /* * Make a new default object. Passing LO_OBJECT will create an * LO_Element, which being a union of all other layout element types * is guaranteed to be big enough to transmogrify into one of these * specific types later if necessary. */ object = (LO_ObjectStruct*) lo_NewElement(context, state, LO_OBJECT, NULL, 0); if (object == NULL) { state->top_state->out_of_memory = TRUE; return; } top = top_state->object_stack; top->object = object; /* * Set up default fields for this object that are common * to all possible object types. */ object->lo_element.type = LO_NONE; object->lo_element.lo_any.ele_id = NEXT_ELEMENT; object->lo_element.lo_any.x = state->x; object->lo_element.lo_any.x_offset = 0; object->lo_element.lo_any.y = state->y; object->lo_element.lo_any.y_offset = 0; object->lo_element.lo_any.width = 0; object->lo_element.lo_any.height = 0; object->lo_element.lo_any.next = NULL; object->lo_element.lo_any.prev = NULL; /* * Now attempt to figure out what type of object we have. * If the type can be determined here, great; otherwise * we have to block until the type can be determined by * reading in additional data. * * Initially the type of the object is LO_NONE. When * we figure out enough to know the type, we set it to * LO_EMBED, LO_JAVA, or LO_IMAGE. If the type had * already been changed to a different incompatible type, * then the tag is malformed and we should ignore it, so * set the type to LO_UNKNOWN. */ #if 0 /* * Check the "codetype" attribute, which optionally determines * the MIME type of the object code itself (as opposed to its * data). The only code type we know about right now is * application/java-vm for Java applets. */ buff = lo_FetchParamValue(context, tag, PARAM_CODETYPE); if (buff != NULL) { PA_LOCK(str, char *, buff); if (pa_TagEqual(APPLICATION_JAVAVM, str)) { /* It's a Java applet */ if (type == LO_NONE) type = LO_JAVA; else if (type != LO_JAVA) type = LO_UNKNOWN; } else if (pa_TagEqual(APPLICATION_OLEOBJECT, str) || pa_TagEqual(APPLICATION_OLEOBJECT2, str)) { /* It's an OLE object */ if (type == LO_NONE) type = LO_EMBED; else if (type != LO_EMBED) type = LO_UNKNOWN; } PA_UNLOCK(buff); XP_FREE(buff); } #endif /* * Check the "classid" attribute, which optionally determines * the specific implementation of the object. The classid * could be a normal URL, in which case we have to retrieve * that URL and match it against a known code type (see above). * There are also two "magic" URL types supported for classid: * "clsid:", which indicates a COM 376-hex-digit class ID, * and "java:", which indicates a specific java class to run. * Note that the "java:" URL is different from the APPLET * "code" attribute: the "java:" URL specifies a particular * method (e.g. "java:program.run"), while the APPLET CODE * attribute specifies an applet subclass (e.g. "MyApplet.class"). * * Further notes about "java:" * We are adding two related "magic" protocol selectors to * augment "java:". These are "javaprogram:" and "javabean:". * They are used with embedded applications and application * objects. "javaprogram:" identifies an object as being a * subclass of netscape.application.Application, and is used * to start an instance of such application. "javabean:" is * used to add an embedded object to an application. */ buff = lo_FetchParamValue(context, tag, PARAM_CLASSID); if (buff != NULL) { PA_LOCK(str, char *, buff); if (XP_STRNCASECMP(str, "clsid:", 6) == 0) { /* * It's a COM class ID, so make sure we have an * appropriate plug-in to handle ActiveX controls. */ if (NPL_FindPluginEnabledForType(APPLICATION_OLEOBJECT) != NULL) { if (type == LO_NONE) type = LO_EMBED; else if (type != LO_EMBED) type = LO_UNKNOWN; } } else if ( (XP_STRNCASECMP(str, "java:", 5) == 0) || (XP_STRNCASECMP(str, "javaprogram:", 12) == 0) || (XP_STRNCASECMP(str, "javabean:", 9) == 0) ) { /* It's a Java class */ if (type == LO_NONE) type = LO_JAVA; else if (type != LO_JAVA) type = LO_UNKNOWN; } else { /* * Must be a URL to the code; we'll need to fetch it to * determine the type. bing: How should we do this? */ } PA_UNLOCK(buff); XP_FREE(buff); } /* * Check the "type" attribute, which optionally determines * the type of the data for the object. The data type * can be used to infer the object implementation type if * the implementation hasn't been specified via "classid" * or "codetype" (see above). The two kinds of objects * we currently support with typed data are plug-ins and * images; for plug-ins we can ask libplug if the type is * currently handled by a plug-in; for images we just check * against a hard-coded list of image types we natively * support (yuck). */ buff = lo_FetchParamValue(context, tag, PARAM_TYPE); if (buff != NULL) { PA_LOCK(str, char *, buff); if (NPL_FindPluginEnabledForType(str) != NULL) { /* It's a plug-in */ if (type == LO_NONE) type = LO_EMBED; else if (type != LO_EMBED) type = LO_UNKNOWN; } /* Adding a check for applets that handle mimetypes. The pref is stored based on the particular mimetype. We do a lookup and if there is an association, the name of the applet is placed into "appletName". NOTE: PREF_CopyCharPref() allocates memory for appletName and we must free it. 9.23.97 amusil */ #ifdef ANTHRAX if((appletName = NPL_FindAppletEnabledForMimetype(str)) != NULL) { /* Set the type */ type = LO_JAVA; /* set the CLASSID to whatever was put into "appletName" */ lo_SetClassID(tag, appletName); /* set this so that we know later to translate the DATA/SRC param to a Java arg */ javaMimetypeHandler = TRUE; XP_FREE(appletName); } #endif /* ANTHRAX */ #if 0 else if (XP_STRNCASECMP(str, "image/", 6) == 0) { if (XP_STRCASECMP(str, IMAGE_GIF) || XP_STRCASECMP(str, IMAGE_JPG) || XP_STRCASECMP(str, IMAGE_PJPG) || XP_STRCASECMP(str, IMAGE_XBM) || XP_STRCASECMP(str, IMAGE_XBM2) || XP_STRCASECMP(str, IMAGE_XBM3)) { /* It's an image */ if (type == LO_NONE) type = LO_IMAGE; else if (type != LO_IMAGE) type = LO_UNKNOWN; } } #endif /* if 0 */ #ifdef SHACK if (XP_STRNCASECMP(str, "builtin", 7) == 0) { if (type == LO_NONE) type = LO_BUILTIN; else if (type != LO_BUILTIN) type = LO_UNKNOWN; } #endif /* SHACK */ PA_UNLOCK(buff); XP_FREE(buff); } #ifdef ANTHRAX else /* we didn't find a TYPE param, so check the filename to get the type - amusil */ { buff = lo_FetchParamValue(context, tag, PARAM_SRC); /* if no src, check if there's a DATA param */ if(buff == NULL) buff = lo_FetchParamValue(context, tag, PARAM_DATA); /* extract the mimetype info */ PA_LOCK(str, char *, buff); fileInfo = NET_cinfo_find_type(str); str = fileInfo->type; if((appletName = NPL_FindAppletEnabledForMimetype(str)) != NULL) { /* Set the type */ type = LO_JAVA; /* set the CLASSID to whatever was put into "appletName" */ lo_SetClassID(tag, appletName); /* set this so that we know later to translate the DATA/SRC param to a Java arg */ javaMimetypeHandler = TRUE; XP_FREE(appletName); /* do we need to free this regardless? */ } if(buff) XP_FREE(buff); } #endif /* ANTRHAX */ if (type == LO_EMBED) { object->lo_element.type = LO_EMBED; } #ifdef SHACK else if (type == LO_BUILTIN) { object->lo_element.type = LO_BUILTIN; } #endif #ifdef JAVA else if (type == LO_JAVA) { if (LJ_GetJavaEnabled() != FALSE) { /* * Close out the current applet if necessary * (people tend to forget ""). */ if (state->current_java != NULL) lo_CloseJavaApp(context, state, state->current_java); object->lo_element.type = LO_JAVA; lo_FormatJavaObject(context, state, tag, (LO_JavaAppStruct*) object); /* If we determined previously that this is an applet to mimetype association, we must set up the SRC or DATA as an arg for the applet. 9.8.97 amusil */ #ifdef ANTHRAX if(javaMimetypeHandler) lo_SetJavaArgs((char*)tag->data, state->current_java); #endif /* ANTHRAX */ } } #endif /* JAVA */ #if 0 else if (type == LO_IMAGE) { object->type = LO_IMAGE; lo_FormatImageObject(context, state, tag, (LO_ImageStruct*) object); } #endif /* if 0 */ else { /* * Check for a "data" attribute; if it exists, we can get * the URL later to see what the type of the object should be. */ buff = lo_FetchParamValue(context, tag, PARAM_DATA); if (buff != NULL) { PA_LOCK(str, char *, buff); if (XP_STRNCASECMP(str, "data:", 5) == 0) { /* bing: deal with magic data URLs here */ PA_UNLOCK(buff); XP_FREE(buff); } else { /* * Block layout until we can read the PARAM tags * and closing tag, go get the data URL, * and determine its type. At that point (in * either LO_NewObjectStream, or lo_ObjectURLExit), * we know the object type and can unblock. */ top->data_url = str; state->top_state->layout_blocking_element = (LO_Element*) object; PA_UNLOCK(buff); /* Don't free buff; we stored it in the object stack */ } } else { /* * Otherwise we just don't know what to do with this! */ object->lo_element.type = LO_UNKNOWN; } } } /* * Takes a "PARAM" tag and pointers to the object's attribute * count, attribute name array, and attribute value array. * Parses the name and value of the PARAM and adjusts the * attribute count, names, and values accordingly. */ void lo_ObjectParam(MWContext* context, lo_DocState* state, PA_Tag* tag, uint32* count, char*** names, char*** values) { PA_Block buff; char *str; char *param_name = NULL; char *param_value = NULL; char **name_list; char **value_list; /* * Get the name of this parameter. */ buff = lo_FetchParamValue(context, tag, PARAM_NAME); if (buff != NULL) { PA_LOCK(str, char *, buff); if (str != NULL) { int32 len; char *new_str; len = lo_StripTextWhitespace(str, XP_STRLEN(str)); new_str = (char *)XP_ALLOC(len + 1); if (new_str != NULL) { XP_STRCPY(new_str, str); } param_name = new_str; } PA_UNLOCK(buff); PA_FREE(buff); } else { /* If we don't have a parameter name, it's useless */ return; } /* * Get the value of this parameter. */ buff = lo_FetchParamValue(context, tag, PARAM_VALUE); if (buff != NULL) { PA_LOCK(str, char *, buff); if (str != NULL) { int32 len; char *new_str; len = lo_StripTextWhitespace(str, XP_STRLEN(str)); new_str = (char *)XP_ALLOC(len + 1); if (new_str != NULL) { XP_STRCPY(new_str, str); } param_value = new_str; } PA_UNLOCK(buff); PA_FREE(buff); } /* * Add the parameter to the name/value list. */ if (*names == NULL) name_list = (char **)XP_ALLOC(sizeof(char *)); else name_list = (char **)XP_REALLOC(*names, ((*count + 1) * sizeof(char *))); /* * If we run out of memory here, free up what * we can and return. */ if (name_list == NULL) { state->top_state->out_of_memory = TRUE; XP_FREE(param_name); if (param_value != NULL) XP_FREE(param_value); return; } if (*values == NULL) value_list = (char **)XP_ALLOC(sizeof(char *)); else value_list = (char **)XP_REALLOC(*values, ((*count + 1) * sizeof(char *))); /* * If we run out of memory here, free up what * we can and return. */ if (value_list == NULL) { state->top_state->out_of_memory = TRUE; XP_FREE(param_name); if (param_value != NULL) XP_FREE(param_value); return; } *names = name_list; *values = value_list; (*names)[*count] = param_name; (*values)[*count] = param_value; (*count)++; } /* * Appends the name/value list in count2, names2, and values2 on * to the name/value list in count1, names1, and values1. */ void lo_AppendParamList(uint32* count1, char*** names1, char*** values1, uint32 count2, char** names2, char** values2) { char** names = NULL; char** values = NULL; if (*names1 != NULL) { if (names2 != NULL) { names = (char**) XP_REALLOC(*names1, ((*count1 + count2) * sizeof(char*))); if (names != NULL) XP_MEMCPY(&(names[*count1]), names2, count2 * sizeof(char*)); } else names = *names1; } else names = names2; if (*values1 != NULL) { if (values2 != NULL) { values = (char**) XP_REALLOC(*values1, ((*count1 + count2) * sizeof(char*))); if (values != NULL) XP_MEMCPY(&(values[*count1]), values2, count2 * sizeof(char*)); } else values = *values1; } else values = values2; if (names != NULL && values != NULL) { *count1 = *count1 + count2; *names1 = names; *values1 = values; } } lo_ObjectStack* lo_PushObject(MWContext* context, lo_DocState* state, PA_Tag* tag) { /* * If possible, reuse an object stack entry created in * a previous pass over this tag (i.e. while blocked, by * lo_BlockObjectTag). If there's no previously-created * object, make a new one. */ lo_TopState* top_state = state->top_state; lo_ObjectStack* new_top = top_state->object_cache; if (new_top != NULL) { /* Find and remove the matching item, if any */ if (new_top->real_tag == tag) top_state->object_cache = new_top->next; else { while (new_top->next != NULL) { if (new_top->next->real_tag == tag) { lo_ObjectStack* temp = new_top->next; new_top->next = new_top->next->next; new_top = temp; break; } new_top = new_top->next; } } } if (new_top == NULL || new_top->real_tag != tag) { new_top = XP_NEW_ZAP(lo_ObjectStack); if (new_top == NULL) { state->top_state->out_of_memory = TRUE; return NULL; } } new_top->next = top_state->object_stack; top_state->object_stack = new_top; new_top->context = context; new_top->state = state; /* * Clone the tag since the tag passed in may be freed * by the parser before we're ready for it. */ if (new_top->real_tag != tag) { new_top->real_tag = tag; if (new_top->clone_tag != NULL) PA_FreeTag(new_top->clone_tag); new_top->clone_tag = XP_NEW(PA_Tag); if (new_top->clone_tag != NULL) { XP_MEMCPY(new_top->clone_tag, tag, sizeof(PA_Tag)); new_top->clone_tag->data = PA_ALLOC((tag->data_len + 1) * sizeof (char)); if (new_top->clone_tag->data != NULL) { char* source; char* dest; PA_LOCK(source, char*, tag->data); PA_LOCK(dest, char*, new_top->clone_tag->data); XP_MEMCPY(dest, source, tag->data_len + 1); PA_UNLOCK(dest); PA_UNLOCK(source); } } } return new_top; } void lo_PopObject(lo_DocState* state) { lo_TopState* top_state = state->top_state; lo_ObjectStack* top = top_state->object_stack; if (top == NULL) return; /* Unlink from stack */ top_state->object_stack = top->next; /* Add to cache */ top->next = top_state->object_cache; top_state->object_cache = top; } void lo_DeleteObjectStack(lo_ObjectStack* top) { /* Delete parameters */ if (top->param_count > 0) { uint32 index; if (top->param_values != NULL) { for (index = 0; index < top->param_count; index++) { if (top->param_names[index] != NULL) XP_FREE(top->param_names[index]); } XP_FREE(top->param_names); } if (top->param_values != NULL) { for (index = 0; index < top->param_count; index++) { if (top->param_values[index] != NULL) XP_FREE(top->param_values[index]); } XP_FREE(top->param_values); } } /* Delete tag copy */ if (top->clone_tag != NULL) PA_FreeTag(top->clone_tag); /* Delete object */ XP_FREE(top); } Bool lo_CheckObjectBlockage(MWContext* context, lo_DocState* state, lo_ObjectStack* top) { LO_ObjectStruct* object = top->object; if (object != NULL && state->top_state->layout_blocking_element == (LO_Element*) object) { /* * We're blocked on this object, and we need to start * a data stream to figure out the object type. Now * that we've read the PARAMs, it's OK to start the * stream. */ if (top->data_url != NULL) { XP_ASSERT(object->lo_element.type == LO_NONE); lo_FetchObjectData(context, state, top); } else { XP_ASSERT(FALSE); object->lo_element.type = LO_UNKNOWN; } return TRUE; /* Yes, we're the cause of the blockage */ } return FALSE; /* No, some enclosing object is the block point */ } void lo_ProcessObjectTag(MWContext* context, lo_DocState* state, PA_Tag* tag, XP_Bool blocked) { lo_TopState* top_state = state->top_state; lo_ObjectStack* top = top_state->object_stack; LO_ObjectStruct* object = top ? top->object : NULL; if (tag->is_end == FALSE) { lo_ObjectStack* new_top; /* * If we have started loading an OBJECT we are * out if the HEAD section of the HTML * and into the BODY */ if (blocked == FALSE) { state->top_state->in_head = FALSE; state->top_state->in_body = TRUE; } /* * Push a new entry onto the stack. If blocked, * also stash it in the tag's lo_data so we can * reuse it later when not blocked. */ new_top = lo_PushObject(context, state, tag); if (blocked) { XP_ASSERT(tag->lo_data == NULL); tag->lo_data = (void*) new_top; } /* * If we're not blocked, and not in the middle of a handled * object, go process this object tag. Otherwise ignore the * the tag (we don't just filter it out in lo_FilterTag * because we need to keep track of the object nesting to * match up with the right ). */ if (blocked == FALSE) { if (object == NULL || object->lo_element.type == LO_UNKNOWN) { lo_FormatObject(context, state, tag); /* * If we've already read the PARAMs, we can try deal * with the blockage now (in fact, we must). If we * haven't read the params yet, we have to wait until * our matching tag (this will be handled * by the code below). */ if (new_top->read_params) lo_CheckObjectBlockage(context, state, new_top); } } } else { if (top != NULL) { if (blocked) { /* * Set a flag indicating that we've already read * this object's params while blocked so we won't * have to do it again later when unblocked. */ top->read_params = TRUE; if (lo_CheckObjectBlockage(context, state, top)) { /* * If we're blocked on this object, don't pop the * stack. When the blockage is cleared we want to * still be at the top of the stack, because layout * will proceed immediately after the block point, * which was our opening tag. */ } else { /* * If blocked on some other (enclosing) object, * pop the stack. */ lo_PopObject(state); } } else { /* * If not blocked, just close out the object * as appropriate. */ if (object != NULL && top->formatted_object == FALSE) { if (object->lo_element.type == LO_EMBED) { lo_FormatEmbedObject(context, state, top->clone_tag, (LO_EmbedStruct*) object, FALSE, /* Stream not started */ top->param_count, top->param_names, top->param_values); top->formatted_object = TRUE; top->param_count = 0; top->param_names = NULL; top->param_values = NULL; } #ifdef SHACK else if (object->lo_element.type == LO_BUILTIN) { lo_FormatBuiltinObject(context, state, top->clone_tag, (LO_BuiltinStruct*) object, FALSE, /* Stream not started */ top->param_count, top->param_names, top->param_values); top->formatted_object = TRUE; top->param_count = 0; top->param_names = NULL; top->param_values = NULL; } #endif #ifdef JAVA else if (object->lo_element.type == LO_JAVA) { lo_CloseJavaApp(context, state, (LO_JavaAppStruct*) object); } #endif } /* Pop stack entry */ lo_PopObject(state); } } } } void lo_ProcessParamTag(MWContext* context, lo_DocState* state, PA_Tag* tag, XP_Bool blocked) { if (tag->is_end == FALSE) { lo_TopState* top_state = state->top_state; lo_ObjectStack* top = top_state->object_stack; /* * PARAMs can be used inside both APPLETs and OBJECTs. * Since applets can be inside objects but objects can't * be inside applets, give the current applet priority * for ownership of the parameter. */ #ifdef JAVA if (state->current_java != NULL && blocked == FALSE) { LO_JavaAppStruct* java_app = state->current_java; lo_ObjectParam(context, state, tag, (uint32*) &(java_app->param_cnt), &(java_app->param_names), &(java_app->param_values)); } else #endif if (top != NULL && top->read_params == FALSE) { lo_ObjectParam(context, state, tag, &(top->param_count), &(top->param_names), &(top->param_values)); } } } /* * This function gets the URL for the "data" attribute * for an object, so we can determine the MIME type of * the data and thus the type of object. If libnet can * get the data, LO_NewObjectStream will be called (see * below). If it can't, lo_ObjectURLExit will be called * (also see below). */ void lo_FetchObjectData(MWContext* context, lo_DocState* state, lo_ObjectStack* top) { char* address = NULL; URL_Struct* urls = NULL; address = NET_MakeAbsoluteURL( state->top_state->base_url, top->data_url); if (address != NULL) { urls = NET_CreateURLStruct(address, NET_DONT_RELOAD); if (urls != NULL) { urls->fe_data = (void*) top; (void) NET_GetURL(urls, FO_CACHE_AND_OBJECT, context, lo_ObjectURLExit); } } if (address != NULL) XP_FREE(address); if (urls == NULL) { state->top_state->out_of_memory = TRUE; return; } } /* * Create a new stream handler for dealing with a stream of * object data. We don't really want to do anything with * the data, we just need to check the type to see if this * is some kind of object we can handle. If it is, we can * format the right kind of object, clear the layout blockage, * and connect this stream up to its rightful owner. * NOTE: Plug-ins are the only object type supported here now. */ NET_StreamClass* LO_NewObjectStream(FO_Present_Types format_out, void* type, URL_Struct* urls, MWContext* context) { NET_StreamClass* stream = NULL; lo_ObjectStack* top = (lo_ObjectStack*) urls->fe_data; if (top != NULL && top->object != NULL) { if (NPL_FindPluginEnabledForType(urls->content_type) != NULL) { /* bing: Internal reference to libplug! */ extern void NPL_EmbedURLExit(URL_Struct*, int, MWContext*); /* Now we know the object type */ top->object->lo_element.type = LO_EMBED; lo_FormatEmbedObject(top->context, top->state, top->clone_tag, (LO_EmbedStruct*) top->object, TRUE, top->param_count, top->param_names, top->param_values); top->param_count = 0; top->param_names = NULL; top->param_values = NULL; top->formatted_object = TRUE; /* Set the FE data that libplug expects */ urls->fe_data = top->object->lo_element.lo_embed.FE_Data; /* Set the exit routine that libplug expects */ NET_SetNewContext(urls, context, NPL_EmbedURLExit); /* Get the stream handler that libplug registered */ stream = NET_StreamBuilder(FO_CACHE_AND_EMBED, urls, context); lo_ClearObjectBlock(top->context, top->object); } } return stream; } /* * This routine is called when a data stream we requested * failed (either because libnet couldn't get the data, or * because we returned a NULL stream in LO_NewObjectStream * because the MIME type of the stream didn't translate into * a handlable object type). In this case we need to get * the object out of the URL, set its type to "unknown", and * unblock layout. This routine is also called when we * successfully handed off the stream to its new owner and * reset the exit routine (in LO_NewObjectStream; the status * will be MK_CHANGING_CONTEXT in the latter case). In this * case, we don't need to do anything here. */ void lo_ObjectURLExit(URL_Struct* urls, int status, MWContext* context) { if (urls != NULL && status != MK_CHANGING_CONTEXT) { if (urls->fe_data != NULL) { lo_ObjectStack* stack = (lo_ObjectStack*) urls->fe_data; LO_ObjectStruct* object = stack->object; if (object != NULL) { object->lo_element.type = LO_UNKNOWN; lo_ClearObjectBlock(context, object); } } XP_FREE(urls); } } void lo_ClearObjectBlock(MWContext* context, LO_ObjectStruct* object) { int32 doc_id = XP_DOCID(context); lo_TopState* top_state = lo_FetchTopState(doc_id); if (top_state != NULL && top_state->doc_state != NULL && top_state->layout_blocking_element == (LO_Element*) object) { lo_DocState* main_doc_state = top_state->doc_state; lo_DocState* state = lo_CurrentSubState(main_doc_state); lo_FlushBlockage(context, state, main_doc_state); } } /* lo_SetClassID ------------- This inserts a CLASSID attribute into the tag->data string with its value set to "appletName". 9.8.97 created amusil */ #ifdef ANTHRAX static void lo_SetClassID(PA_Tag* tag, char* appletName) { uint32 appletNameLen, oldTagLen, newTagLen; char* tagData; char foo; appletNameLen = XP_STRLEN(appletName); oldTagLen = XP_STRLEN((char*)tag->data); newTagLen = oldTagLen + 15 + appletNameLen; tag->data = XP_REALLOC(tag->data, newTagLen+1); /* remove the '>' character */ tagData = (char*) (tag->data); tagData[oldTagLen-1] = 0; /* add " CLASSID = appletName" */ XP_STRCAT((char*)tag->data, " CLASSID=java:"); XP_STRCAT((char*)tag->data, appletName); XP_STRCAT((char*)tag->data, " >"); tag->data_len = newTagLen; } /* lo_GetNextName() ---------------- This function parses a tag and returns a newly allocated string containing the first encountered NAME. An example of what would get passed to this function is (basically a tag without the preceding " This would return foo. The tag pointer also gets incremented past the just-read NAME. The example above would exit with *tag equal to: " = VALUE bar = VALUE2 >" NULL is returned if a '>' character is encountered. 10.7.97 amusil */ static char* lo_GetNextName(char** tag) { uint32 nameLength = 0; char* newName; char* start; XP_ASSERT(tag); XP_ASSERT(*tag); XP_ASSERT(**tag); while(**tag == ' ') ++(*tag); if(**tag == '>') return NULL; start = *tag; while((**tag != ' ') && (**tag != '=')) { ++nameLength; ++(*tag); } newName = XP_ALLOC((nameLength+1)*sizeof(char)); newName = strncpy(newName, start, nameLength); XP_ASSERT(newName); newName[nameLength] = 0; return newName; } /* lo_GetNextValue() ---------------- This function parses a tag and returns a newly allocated string containing the first encountered VALUE. An example of what would get passed to this function is (basically a tag without the preceding " This would return foo. The tag pointer also gets incremented past the just-read VALUE. The example above would exit with *tag equal to: " NAME = bar >" In the case that the value is enclosed in quotes, the entire string within the quotes, including spaces, is returned. NOTE: This function expects that lo_GetNextName() was called before it. 10.7.97 amusil */ static char* lo_GetNextValue(char** tag) { uint32 valueLength = 0; char* newValue; char* start; XP_ASSERT(tag); XP_ASSERT(*tag); XP_ASSERT(**tag); while(**tag == ' ' || **tag == '=') ++(*tag); if(**tag == '>') return NULL; /* if the value is enclosed in quotes, we capture the entire string up to the closing quote */ if(**tag == '\"') { ++(*tag); start = *tag; while(**tag != '\"') { ++valueLength; ++(*tag); } ++(*tag); /* skip over the enclosing " */ } else /* otherwise, we just get up to the first space */ { start = *tag; while((**tag != ' ')) { ++valueLength; ++(*tag); } } newValue = XP_ALLOC((valueLength+1)*sizeof(char)); newValue = strncpy(newValue, start, valueLength); XP_ASSERT(newValue); newValue[valueLength] = 0; return newValue; } /* lo_SetJavaArgs -------------- Extracts every param and passes them as arguements to the Java applet. Here is the mapping: <... X=Y ...> becomes equivalent to NOTE: lo_GetNextName() lo_GetNextValue() and allocates new memory for the return values. 10.7.97 amusil */ #ifdef JAVA static void lo_SetJavaArgs(char* tag, LO_JavaAppStruct* current_java) { char* paramName; char* paramValue; char* index; Bool is_percent; int32 val; index = tag; while(TRUE) { paramName = lo_GetNextName(&index); if(paramName == NULL) break; /* always map data to src */ if(!strcasecomp(paramName, "data")) { XP_FREE(paramName); paramName = XP_STRDUP("src"); } paramValue = lo_GetNextValue(&index); /* Check if the user specified a % for height and width */ if(!strcasecmp(paramName, "height")) { val = lo_ValueOrPercent(paramValue, &is_percent); if(is_percent) { XP_FREE(paramValue); /* int32 cannot exceed 10 digits */ paramValue = XP_ALLOC(11 * sizeof(char)); lo_itoa(current_java->height, paramValue); } } if(!strcasecmp(paramName, "width")) { val = lo_ValueOrPercent(paramValue, &is_percent); if(is_percent) { XP_FREE(paramValue); /* int32 cannot exceed 10 digits */ paramValue = XP_ALLOC(11 * sizeof(char)); lo_itoa(current_java->width, paramValue); } } /* increment and resize array */ ++(current_java->param_cnt); current_java->param_names = XP_REALLOC(current_java->param_names, current_java->param_cnt*sizeof(char*)); XP_ASSERT(current_java->param_names); current_java->param_values = XP_REALLOC(current_java->param_values, current_java->param_cnt*sizeof(char*)); XP_ASSERT(current_java->param_values); /* point the new array elements to the newly allocated paramName and paramValue */ current_java->param_names[current_java->param_cnt-1] = paramName; current_java->param_values[current_java->param_cnt-1] = paramValue; } } #endif static void lo_itoa(uint32 n, char* s) { int i, sign; if((sign = n) < 0) n = -n; i = 0; do { s[i++] = n%10 + '0'; } while ((n/=10) > 0); if(sign < 0) s[i++] = '-'; s[i] = '\0'; lo_ReverseString(s); } static void lo_ReverseString(char* s) { int c, i, j; for(i=0, j=strlen(s)-1; i