netscape-revival
lib/layout/laymocha.c
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*-
*/
#ifdef MOCHA
/*
* Mocha layout interface.
*/
#include "xp.h"
#include "lo_ele.h"
#include "layout.h"
#include "libmocha.h"
#include "pa_parse.h"
#ifdef JAVA
#include "mo_java.h"
#endif
void
lo_MarkMochaTopState(lo_TopState *top_state, MWContext *context)
{
MochaDecoder *decoder;
if (top_state == NULL || top_state->mocha_mark)
return;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return;
LM_PutMochaDecoder(decoder);
top_state->mocha_mark = TRUE;
}
lo_TopState *
lo_GetTopState(MWContext *context)
{
int32 doc_id;
lo_TopState *top_state;
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if (top_state != NULL && top_state->doc_state == NULL)
return NULL;
return top_state;
}
lo_TopState *
lo_GetMochaTopState(MWContext *context)
{
lo_TopState *top_state;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
lo_MarkMochaTopState(top_state, context);
return top_state;
}
lo_FormData *
LO_GetFormDataByID(MWContext *context, intn form_id)
{
lo_TopState *top_state;
lo_FormData *form;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
for (form = top_state->form_list; form != NULL; form = form->next) {
if (form->id == form_id)
return form;
}
return NULL;
}
uint
LO_EnumerateForms(MWContext *context)
{
lo_TopState *top_state;
lo_FormData *form;
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
return 0;
/* Try to reflect all forms in case someone is enumerating. */
for (form = top_state->form_list; form != NULL; form = form->next) {
if (form->mocha_object == NULL)
(void) LM_ReflectForm(context, form);
}
return top_state->current_form_num;
}
/*
* Note that index is zero-based from the first form, and if in the second
* form, is one more than the index of the last element in the first form.
* So we subtract form's first element index before subscripting.
*/
LO_FormElementStruct *
LO_GetFormElementByIndex(lo_FormData *form, int32 index)
{
LO_Element **ele_list;
LO_FormElementStruct *form_element;
if (form->form_elements == NULL)
return NULL;
PA_LOCK(ele_list, LO_Element **, form->form_elements);
form_element = (LO_FormElementStruct *)ele_list[0];
index -= form_element->element_index;
if ((uint32)index < (uint32)form->form_ele_cnt)
form_element = (LO_FormElementStruct *)ele_list[index];
else
form_element = NULL;
PA_UNLOCK(form->form_elements);
if (form_element == NULL || form_element->element_data == NULL)
return NULL;
return form_element;
}
uint
LO_EnumerateFormElements(MWContext *context, lo_FormData *form)
{
LO_Element **ele_list;
LO_FormElementStruct *form_element;
uint i;
/* Try to reflect all elements in case someone is enumerating. */
PA_LOCK(ele_list, LO_Element **, form->form_elements);
for (i = 0; i < (uint)form->form_ele_cnt; i++) {
form_element = (LO_FormElementStruct *)ele_list[i];
if (form_element->mocha_object == NULL)
(void) LM_ReflectFormElement(context, form_element);
}
PA_UNLOCK(form->form_elements);
return form->form_ele_cnt;
}
static Bool
lo_compile_mocha_param(MWContext *context, PA_Tag *tag, MochaObject *object,
char *name, PA_Block block)
{
MochaDecoder *decoder;
char *body;
Bool ok;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return FALSE;
PA_LOCK(body, char *, block);
ok = MOCHA_CompileMethod(decoder->mocha_context, object, name, 0,
body, XP_STRLEN(body), LM_GetSourceURL(decoder),
tag->newline_count);
PA_UNLOCK(block);
LM_PutMochaDecoder(decoder);
return ok;
}
void
lo_BeginReflectForm(MWContext *context, lo_DocState *doc_state, PA_Tag *tag,
lo_FormData *form_data)
{
PA_Block name, onreset, onsubmit;
MochaObject *form_object;
MochaDecoder *decoder;
name = lo_FetchParamValue(context, tag, PARAM_NAME);
onreset = lo_FetchParamValue(context, tag, PARAM_ONRESET);
onsubmit = lo_FetchParamValue(context, tag, PARAM_ONSUBMIT);
if (name == NULL && onreset == NULL && onsubmit == NULL)
return;
lo_MarkMochaTopState(doc_state->top_state, context);
/* XXX begin common */
form_object = LM_ReflectForm(context, form_data);
if (form_object) {
decoder = LM_GetMochaDecoder(context);
if (decoder) {
decoder->active_form = form_object;
LM_PutMochaDecoder(decoder);
}
/* XXX end common */
if (onreset != NULL) {
(void) lo_compile_mocha_param(context, tag, form_object,
PARAM_ONRESET, onreset);
}
if (onsubmit != NULL) {
(void) lo_compile_mocha_param(context, tag, form_object,
PARAM_ONSUBMIT, onsubmit);
}
}
if (name != NULL)
PA_FREE(name);
if (onreset != NULL)
PA_FREE(onreset);
if (onsubmit != NULL)
PA_FREE(onsubmit);
}
LO_ImageStruct *
LO_GetImageByIndex(MWContext *context, intn index)
{
lo_TopState *top_state;
LO_ImageStruct *image;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
for (image = top_state->image_list; image != NULL;
image = image->next_image) {
if (!index--)
return image;
}
return NULL;
}
uint
LO_EnumerateImages(MWContext *context)
{
lo_TopState *top_state;
uint image_num;
LO_ImageStruct *image;
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
return 0;
/* Try to reflect all images in case someone is enumerating. */
image_num = 0;
for (image = top_state->image_list; image != NULL;
image = image->next_image) {
if (image->mocha_object == NULL)
(void) LM_ReflectImage(context, image, NULL, image_num);
image_num++;
}
return image_num;
}
void
lo_EndReflectForm(MWContext *context, lo_FormData *form_data)
{
MochaDecoder *decoder;
if (!context->mocha_context)
return;
decoder = LM_GetMochaDecoder(context);
if (decoder) {
XP_ASSERT(form_data->mocha_object == decoder->active_form);
decoder->active_form = NULL;
LM_PutMochaDecoder(decoder);
}
}
void
lo_ReflectImage(MWContext *context, lo_DocState *doc_state, PA_Tag *tag,
LO_ImageStruct *image, Bool blocked)
{
PA_Block name, onload, onabort, onerror;
uint index;
MochaObject *image_object;
lo_TopState *top_state = doc_state->top_state;
/* Reflect only IMG tags, not form INPUT tags of TYPE=IMAGE */
if (tag->type != P_IMAGE && tag->type != P_NEW_IMAGE)
return;
if (image->mocha_object) {
if (! blocked) {
/* Flush any image events that were sent when the image
was blocked */
LM_SendImageEvent(context, image, LM_IMGUNBLOCK);
}
return;
}
/* Add image to global list of images for this document. */
if (! (image->image_attr->attrmask & LO_ATTR_ON_IMAGE_LIST)) {
image->image_attr->attrmask |= LO_ATTR_ON_IMAGE_LIST;
if (top_state->last_image)
top_state->last_image->next_image = image;
else
top_state->image_list = image;
top_state->last_image = image;
index = (uint)top_state->image_count++;
}
/* The image is reflected into a Javascript object lazily:
Immediate reflection only occurs if there are Javascript-only
attributes in the IMG tag */
name = lo_FetchParamValue(context, tag, PARAM_NAME);
onload = lo_FetchParamValue(context, tag, PARAM_ONLOAD);
onabort = lo_FetchParamValue(context, tag, PARAM_ONABORT);
onerror = lo_FetchParamValue(context, tag, PARAM_ONERROR);
if (name == NULL && onload == NULL && onabort == NULL && onerror == NULL)
return;
lo_MarkMochaTopState(top_state, context);
image_object = LM_ReflectImage(context, image, (char*)name, index);
if (image_object) {
if (onload != NULL)
(void) lo_compile_mocha_param(context, tag, image_object,
PARAM_ONLOAD, onload);
if (onabort != NULL)
(void) lo_compile_mocha_param(context, tag, image_object,
PARAM_ONABORT, onabort);
if (onerror != NULL)
(void) lo_compile_mocha_param(context, tag, image_object,
PARAM_ONERROR, onerror);
/* Since the image is unblocked, there is no need to buffer
any JavaScript image events. Deliver them as they're generated. */
if (! blocked)
LM_SendImageEvent(context, image, LM_IMGUNBLOCK);
}
if (name != NULL)
PA_FREE(name);
if (onload != NULL)
PA_FREE(onload);
if (onabort != NULL)
PA_FREE(onabort);
if (onerror != NULL)
PA_FREE(onerror);
}
void
lo_CompileEventHandler(MWContext *context, lo_DocState *doc_state, PA_Tag *tag,
LO_FormElementStruct *form_element, char *name)
{
PA_Block block;
MochaObject *element_object;
lo_FormData *form_data;
MochaObject *form_object;
MochaDecoder *decoder;
/* Don't reflect form elements twice, it makes arrays out of them! */
if (doc_state->in_relayout)
return;
block = lo_FetchParamValue(context, tag, name);
if (block == NULL)
return;
lo_MarkMochaTopState(doc_state->top_state, context);
element_object = form_element->mocha_object;
if (element_object == NULL) {
form_data = doc_state->top_state->form_list;
if (form_data != NULL) {
/* XXX begin common */
form_object = LM_ReflectForm(context, form_data);
if (form_object) {
decoder = LM_GetMochaDecoder(context);
if (decoder) {
decoder->active_form = form_object;
LM_PutMochaDecoder(decoder);
}
}
/* XXX end common */
}
element_object = LM_ReflectFormElement(context, form_element);
}
if (element_object != NULL) {
(void) lo_compile_mocha_param(context, tag, element_object,
name, block);
}
PA_FREE(block);
}
void
lo_ReflectNamedAnchor(MWContext *context, lo_DocState *doc_state, PA_Tag *tag,
lo_NameList *name_rec)
{
uint count;
MochaObject *object;
lo_MarkMochaTopState(doc_state->top_state, context);
count = LO_EnumerateNamedAnchors(context);
object = LM_ReflectNamedAnchor(context, name_rec, count - 1);
/* XXX */
}
lo_NameList *
LO_GetNamedAnchorByIndex(MWContext *context, uint index)
{
lo_TopState *top_state;
lo_DocState *doc_state;
lo_NameList *name_rec;
uint count;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
doc_state = top_state->doc_state;
/* The list is in reverse-source order, so we have count anchors. */
count = 0;
for (name_rec = doc_state->name_list; name_rec != NULL;
name_rec = name_rec->next) {
count++;
}
if (count <= index)
return NULL;
count -= index;
for (name_rec = doc_state->name_list; name_rec != NULL;
name_rec = name_rec->next) {
if (--count == 0)
break;
}
return name_rec;
}
uint
LO_EnumerateNamedAnchors(MWContext *context)
{
lo_TopState *top_state;
lo_DocState *doc_state;
lo_NameList *name_rec;
uint count;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return 0;
doc_state = top_state->doc_state;
count = 0;
for (name_rec = doc_state->name_list; name_rec != NULL;
name_rec = name_rec->next) {
count++;
}
return count;
}
void
lo_ReflectLink(MWContext *context, lo_DocState *doc_state, PA_Tag *tag,
LO_AnchorData *anchor_data, uint index)
{
PA_Block onclick, onmouseover, onmouseout;
MochaObject *object;
onclick = lo_FetchParamValue(context, tag, PARAM_ONCLICK);
onmouseover = lo_FetchParamValue(context, tag, PARAM_ONMOUSEOVER);
onmouseout = lo_FetchParamValue(context, tag, PARAM_ONMOUSEOUT);
if (onclick || onmouseover || onmouseout) {
lo_MarkMochaTopState(doc_state->top_state, context);
object = LM_ReflectLink(context, anchor_data, index);
} else {
object = NULL;
}
if (onclick) {
if (object) {
(void) lo_compile_mocha_param(context, tag, object,
PARAM_ONCLICK, onclick);
}
PA_FREE(onclick);
}
if (onmouseover) {
if (object) {
(void) lo_compile_mocha_param(context, tag, object,
PARAM_ONMOUSEOVER, onmouseover);
}
PA_FREE(onmouseover);
}
if (onmouseout) {
if (object) {
(void) lo_compile_mocha_param(context, tag, object,
PARAM_ONMOUSEOUT, onmouseout);
}
PA_FREE(onmouseout);
}
}
LO_AnchorData *
LO_GetLinkByIndex(MWContext *context, uint index)
{
lo_TopState *top_state;
LO_AnchorData **anchor_array;
LO_AnchorData *anchor_data;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
if (index >= (uint)top_state->url_list_len)
return NULL;
XP_LOCK_BLOCK(anchor_array, LO_AnchorData **, top_state->url_list);
anchor_data = anchor_array[index];
XP_UNLOCK_BLOCK(top_state->url_list);
return anchor_data;
}
uint
LO_EnumerateLinks(MWContext *context)
{
lo_TopState *top_state;
uint count, index;
LO_AnchorData **anchor_array;
LO_AnchorData *anchor_data;
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
return 0;
/* Try to reflect all links in case someone is enumerating. */
count = (uint)top_state->url_list_len;
XP_LOCK_BLOCK(anchor_array, LO_AnchorData **, top_state->url_list);
for (index = 0; index < count; index++) {
anchor_data = anchor_array[index];
if (anchor_data->mocha_object == NULL)
(void) LM_ReflectLink(context, anchor_data, index);
}
XP_UNLOCK_BLOCK(top_state->url_list);
return count;
}
#ifdef JAVA
LO_JavaAppStruct *
LO_GetAppletByIndex(MWContext *context, uint index)
{
lo_TopState *top_state;
LO_JavaAppStruct *applet;
int i, count;
/* XXX */
if (!MOCHA_JavaGlueEnabled())
return NULL;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
/* count 'em */
count = 0;
applet = top_state->applet_list;
while (applet) {
applet = applet->nextApplet;
count++;
}
/* reverse order... */
applet = top_state->applet_list;
for (i = count-1; i >= 0; i--) {
if (i == index)
return applet;
applet = applet->nextApplet;
}
return NULL;
}
uint
LO_EnumerateApplets(MWContext *context)
{
lo_TopState *top_state;
int count, index;
LO_JavaAppStruct *applet;
/* XXX */
if (!MOCHA_JavaGlueEnabled())
return 0;
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
return 0;
/* count 'em */
count = 0;
applet = top_state->applet_list;
while (applet) {
applet = applet->nextApplet;
count++;
}
/* reflect all applets in reverse order */
applet = top_state->applet_list;
for (index = count-1; index >= 0; index--) {
if (applet->mocha_object == NULL) {
(void) LM_ReflectApplet(context, applet, index);
}
applet = applet->nextApplet;
}
return count;
}
LO_EmbedStruct *
LO_GetEmbedByIndex(MWContext *context, uint index)
{
lo_TopState *top_state;
LO_EmbedStruct *embed;
int i, count;
/* XXX */
if (!MOCHA_JavaGlueEnabled())
return NULL;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return NULL;
/* count 'em */
count = 0;
embed = top_state->embed_list;
while (embed) {
embed = embed->nextEmbed;
count++;
}
/* reverse order... */
embed = top_state->embed_list;
for (i = count-1; i >= 0; i--) {
if (i == index)
return embed;
embed = embed->nextEmbed;
}
return NULL;
}
uint
LO_EnumerateEmbeds(MWContext *context)
{
lo_TopState *top_state;
int count, index;
LO_EmbedStruct *embed;
/* XXX */
if (!MOCHA_JavaGlueEnabled())
return 0;
top_state = lo_GetMochaTopState(context);
if (top_state == NULL)
return 0;
/* count 'em */
count = 0;
embed = top_state->embed_list;
while (embed) {
embed = embed->nextEmbed;
count++;
}
/* reflect all embeds in reverse order */
embed = top_state->embed_list;
for (index = count-1; index >= 0; index--) {
if (embed->mocha_object == NULL) {
(void) LM_ReflectEmbed(context, embed, index);
}
embed = embed->nextEmbed;
}
return count;
}
#endif /* JAVA */
/* XXX lo_DocState should use a colors[LO_NCOLORS] array to shrink code here
and in layout.c.
*/
void
LO_GetDocumentColor(MWContext *context, int type, LO_Color *color)
{
lo_TopState *top_state;
lo_DocState *doc_state;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return;
doc_state = top_state->doc_state;
switch (type) {
case LO_COLOR_BG:
*color = doc_state->text_bg;
break;
case LO_COLOR_FG:
*color = doc_state->text_fg;
break;
case LO_COLOR_LINK:
*color = doc_state->anchor_color;
break;
case LO_COLOR_VLINK:
*color = doc_state->visited_anchor_color;
break;
case LO_COLOR_ALINK:
*color = doc_state->active_anchor_color;
break;
default:
break;
}
}
void
LO_SetDocumentColor(MWContext *context, int type, LO_Color *color)
{
lo_TopState *top_state;
lo_DocState *doc_state;
top_state = lo_GetTopState(context);
if (top_state == NULL)
return;
doc_state = top_state->doc_state;
if (color == NULL)
color = &lo_master_colors[type];
switch (type) {
case LO_COLOR_BG:
doc_state->text_bg = *color;
top_state->doc_specified_bg = TRUE;
lo_RecolorText(doc_state);
FE_SetBackgroundColor(context, color->red, color->green, color->blue);
break;
case LO_COLOR_FG:
doc_state->text_fg = *color;
lo_RecolorText(doc_state);
break;
case LO_COLOR_LINK:
doc_state->anchor_color = *color;
break;
case LO_COLOR_VLINK:
doc_state->visited_anchor_color = *color;
break;
case LO_COLOR_ALINK:
doc_state->active_anchor_color = *color;
break;
default:;
}
}
void
lo_ProcessContextEventHandlers(MWContext *context, lo_DocState *doc_state,
PA_Tag *tag)
{
PA_Block onload, onunload, onfocus, onblur;
MochaDecoder *decoder;
lo_TopState *top_state;
onload = lo_FetchParamValue(context, tag, PARAM_ONLOAD);
onunload = lo_FetchParamValue(context, tag, PARAM_ONUNLOAD);
onfocus = lo_FetchParamValue(context, tag, PARAM_ONFOCUS);
onblur = lo_FetchParamValue(context, tag, PARAM_ONBLUR);
if (onload == NULL && onunload == NULL && onfocus == NULL && onblur == NULL)
return;
decoder = LM_GetMochaDecoder(context);
if (decoder != NULL) {
top_state = doc_state->top_state;
lo_MarkMochaTopState(top_state, context);
if (onload != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONLOAD, onload);
if (tag->type == P_GRID) {
top_state->savedData.OnLoad = onload;
onload = NULL;
} else if (tag->type == P_BODY) {
top_state->mocha_has_onload = TRUE;
}
}
if (onunload != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONUNLOAD, onunload);
if (tag->type == P_GRID) {
top_state->savedData.OnUnload = onunload;
onunload = NULL;
}
}
if (onfocus != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONFOCUS, onfocus);
if (tag->type == P_GRID) {
top_state->savedData.OnFocus = onfocus;
onfocus = NULL;
}
}
if (onblur != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONBLUR, onblur);
if (tag->type == P_GRID) {
top_state->savedData.OnBlur = onblur;
onblur = NULL;
}
}
LM_PutMochaDecoder(decoder);
}
if (onload != NULL)
PA_FREE(onload);
if (onunload != NULL)
PA_FREE(onunload);
if (onfocus != NULL)
PA_FREE(onfocus);
if (onblur != NULL)
PA_FREE(onblur);
}
void
lo_RestoreContextEventHandlers(MWContext *context, lo_DocState *doc_state,
PA_Tag *tag, SHIST_SavedData *saved_data)
{
PA_Block onload, onunload, onfocus, onblur;
MochaDecoder *decoder;
lo_TopState *top_state;
onload = saved_data->OnLoad;
onunload = saved_data->OnUnload;
onfocus = saved_data->OnFocus;
onblur = saved_data->OnBlur;
if (onload == NULL && onunload == NULL && onfocus == NULL && onblur == NULL)
return;
decoder = LM_GetMochaDecoder(context);
if (decoder == NULL)
return;
top_state = doc_state->top_state;
lo_MarkMochaTopState(top_state, context);
if (onload != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONLOAD, onload);
top_state->savedData.OnLoad = onload;
}
if (onunload != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONUNLOAD, onunload);
top_state->savedData.OnUnload = onunload;
}
if (onfocus != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONFOCUS, onfocus);
top_state->savedData.OnFocus = onfocus;
}
if (onblur != NULL) {
(void) lo_compile_mocha_param(context, tag, decoder->window_object,
PARAM_ONBLUR, onblur);
top_state->savedData.OnBlur = onblur;
}
LM_PutMochaDecoder(decoder);
}
#endif /* MOCHA */