зеркало из https://github.com/mozilla/pjs.git
587 строки
12 KiB
C
587 строки
12 KiB
C
/* -*- 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.
|
|
*/
|
|
|
|
/* styleStack used to hold a stack of tags and associated styles
|
|
*
|
|
* Designed and Implemented by Lou Montulli '97
|
|
*/
|
|
|
|
|
|
#include "xp.h"
|
|
#include "libmocha.h"
|
|
#include "stystruc.h"
|
|
#include "stystack.h"
|
|
#include "jsspriv.h"
|
|
|
|
/* simple stack implementation for style/tag stack
|
|
* why don't we have an XP Stack?
|
|
*/
|
|
#define INITIAL_STACK_SIZE 10
|
|
#define INCREASE_STACK_BY 5
|
|
|
|
typedef struct {
|
|
|
|
StyleStruct *style;
|
|
TagStruct *tag;
|
|
|
|
} TagAndStyleAssoc;
|
|
|
|
typedef struct _SML_StyleAndTagStack {
|
|
StyleAndTagStackInterface *vtable;
|
|
int32 refcount;
|
|
|
|
/* private data */
|
|
XP_Bool save_stack;
|
|
TagAndStyleAssoc **tag_stack;
|
|
int32 tag_stack_size;
|
|
int32 tag_stack_first_unused_index;
|
|
|
|
MWContext *context;
|
|
StyleObject *tags;
|
|
StyleObject *classes;
|
|
StyleObject *ids;
|
|
|
|
} SML_StyleAndTagStack;
|
|
|
|
/* specify whether to keep the stack or not.
|
|
* when the stack is OFF (as it is at initalization)
|
|
* adding to the stack is a noop
|
|
*/
|
|
void
|
|
SML_SetSaveOn(SML_StyleAndTagStack *self, XP_Bool on_flag)
|
|
{
|
|
self->save_stack = on_flag;
|
|
}
|
|
|
|
XP_Bool
|
|
SML_IsSaveOn(SML_StyleAndTagStack *self)
|
|
{
|
|
return self->save_stack;
|
|
}
|
|
|
|
/*
|
|
* allocate TagStrucs from a pool to speed up
|
|
* the application
|
|
*/
|
|
static XP_AllocStructInfo TagStructAlloc =
|
|
{ XP_INITIALIZE_ALLOCSTRUCTINFO(sizeof(TagStruct)) };
|
|
|
|
TagStruct *
|
|
SML_NewTagStruct(SML_StyleAndTagStack *self, char *name, char *class_name, char *id)
|
|
{
|
|
TagStruct *tag;
|
|
|
|
if(!name)
|
|
return NULL;
|
|
|
|
/*
|
|
* allocate TagStrucs from a pool to speed up
|
|
* the application
|
|
*/
|
|
tag = (TagStruct*) XP_AllocStructZero(&TagStructAlloc);
|
|
if(!tag)
|
|
return(NULL);
|
|
|
|
|
|
tag->name = name;
|
|
if(class_name)
|
|
tag->class_name = class_name;
|
|
if(id)
|
|
tag->id = id;
|
|
|
|
return(tag);
|
|
}
|
|
|
|
void
|
|
SML_FreeTagStruct(SML_StyleAndTagStack *self, TagStruct *tag)
|
|
{
|
|
if(!tag)
|
|
return;
|
|
|
|
XP_FREEIF(tag->name);
|
|
XP_FREEIF(tag->class_name);
|
|
XP_FREEIF(tag->id);
|
|
/*
|
|
* return the TagStruct to the pool
|
|
*/
|
|
XP_FreeStruct(&TagStructAlloc, tag);
|
|
tag = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* allocate TagAndStyleAssoc from a pool to speed up
|
|
* the application
|
|
*/
|
|
static XP_AllocStructInfo TagAndStyleAssocAlloc =
|
|
{ XP_INITIALIZE_ALLOCSTRUCTINFO(sizeof(TagAndStyleAssoc)) };
|
|
|
|
TagAndStyleAssoc *
|
|
sml_new_assoc(SML_StyleAndTagStack *self, TagStruct *tag, StyleStruct *style)
|
|
{
|
|
TagAndStyleAssoc *new_assoc;
|
|
|
|
if(!tag || !style)
|
|
{
|
|
XP_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* allocate TagAndStyleAssoc from a pool to speed up
|
|
* the application
|
|
*/
|
|
new_assoc = (TagAndStyleAssoc*) XP_AllocStructZero(&TagAndStyleAssocAlloc);
|
|
|
|
if(!new_assoc)
|
|
return NULL;
|
|
|
|
new_assoc->tag = tag;
|
|
new_assoc->style = style;
|
|
|
|
return new_assoc;
|
|
}
|
|
|
|
void
|
|
sml_free_TagAndStyleAssoc(SML_StyleAndTagStack *self,TagAndStyleAssoc *assoc)
|
|
{
|
|
if(!assoc)
|
|
return;
|
|
|
|
SML_FreeTagStruct(self, assoc->tag);
|
|
STYLESTRUCT_Delete(assoc->style);
|
|
|
|
#ifdef DEBUG
|
|
assoc->tag = (TagStruct *)-1;
|
|
assoc->style = (StyleStruct *)-1;
|
|
#endif
|
|
|
|
/*
|
|
* return the TagAndStyleAssoc to the pool
|
|
*/
|
|
XP_FreeStruct(&TagAndStyleAssocAlloc, assoc);
|
|
assoc = NULL;
|
|
}
|
|
|
|
void
|
|
sml_free_stack(SML_StyleAndTagStack *self)
|
|
{
|
|
int i;
|
|
|
|
if(self->tag_stack)
|
|
{
|
|
for(i = 0; i < self->tag_stack_first_unused_index; i++)
|
|
{
|
|
sml_free_TagAndStyleAssoc(self, self->tag_stack[i]);
|
|
}
|
|
|
|
XP_FREE(self->tag_stack);
|
|
}
|
|
|
|
self->tag_stack_size = 0;
|
|
self->tag_stack_first_unused_index = 0;
|
|
self->tag_stack = NULL;
|
|
|
|
}
|
|
|
|
|
|
void
|
|
sml_expand_stack(SML_StyleAndTagStack *self)
|
|
{
|
|
if(self->tag_stack)
|
|
{
|
|
self->tag_stack_size += INCREASE_STACK_BY;
|
|
self->tag_stack = XP_REALLOC(self->tag_stack,
|
|
self->tag_stack_size*sizeof(TagAndStyleAssoc*));
|
|
}
|
|
else
|
|
{
|
|
self->tag_stack_size = INITIAL_STACK_SIZE;
|
|
self->tag_stack = XP_CALLOC(self->tag_stack_size, sizeof(TagAndStyleAssoc*));
|
|
}
|
|
|
|
if(!self->tag_stack)
|
|
{
|
|
self->tag_stack_first_unused_index = 0;
|
|
self->tag_stack_size = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
ss_add_to_stack(SML_StyleAndTagStack *self, TagAndStyleAssoc *pair)
|
|
{
|
|
if(self->tag_stack_size <= self->tag_stack_first_unused_index)
|
|
sml_expand_stack(self);
|
|
|
|
if(self->tag_stack)
|
|
{
|
|
self->tag_stack[self->tag_stack_first_unused_index++] = pair;
|
|
}
|
|
else
|
|
{
|
|
sml_free_TagAndStyleAssoc(self, pair);
|
|
}
|
|
}
|
|
|
|
|
|
PushTagStatus
|
|
SML_PushTag(SML_StyleAndTagStack *self, char *name, char *class_name, char *id)
|
|
{
|
|
TagStruct *new_tag;
|
|
StyleStruct *ss;
|
|
TagAndStyleAssoc *assoc;
|
|
|
|
if(!self->save_stack)
|
|
return PUSH_TAG_ERROR;
|
|
|
|
new_tag = SML_NewTagStruct(self, name, class_name, id);
|
|
if(!new_tag)
|
|
return PUSH_TAG_ERROR;
|
|
|
|
ss = STYLESTRUCT_Factory_Create();
|
|
|
|
if(!ss)
|
|
{
|
|
SML_FreeTagStruct(self, new_tag);
|
|
return PUSH_TAG_ERROR;
|
|
}
|
|
|
|
assoc = sml_new_assoc(self, new_tag, ss);
|
|
|
|
if(!assoc)
|
|
{
|
|
SML_FreeTagStruct(self, new_tag);
|
|
STYLESTRUCT_Delete(ss);
|
|
return PUSH_TAG_ERROR;
|
|
}
|
|
|
|
ss_add_to_stack(self, assoc);
|
|
|
|
/* add call to style sheet parser here to fill in style struct */
|
|
jss_GetStyleForTopTag((StyleAndTagStack*)self);
|
|
|
|
return(PUSH_TAG_SUCCESS);
|
|
}
|
|
|
|
/* pop a tag from within the stack
|
|
* The stack is indexed from the top element.
|
|
* the zero'th element is the top of the stack
|
|
*/
|
|
void
|
|
SML_PopTagByIndex(SML_StyleAndTagStack *self, char *name, int32 index)
|
|
{
|
|
|
|
TagAndStyleAssoc *assoc;
|
|
/* index from the top of the stack */
|
|
int32 real_index = self->tag_stack_first_unused_index - 1 - index;
|
|
|
|
if(self->tag_stack_first_unused_index < real_index+1 || real_index < 0)
|
|
return;
|
|
|
|
assoc = self->tag_stack[real_index];
|
|
sml_free_TagAndStyleAssoc(self, assoc);
|
|
|
|
/* shift the stack down */
|
|
if(real_index < self->tag_stack_first_unused_index-1)
|
|
XP_MEMMOVE(&self->tag_stack[real_index],
|
|
&self->tag_stack[real_index+1],
|
|
index*sizeof(TagAndStyleAssoc*));
|
|
|
|
/* zero last unused index for debugging purposes */
|
|
self->tag_stack_first_unused_index--;
|
|
self->tag_stack[self->tag_stack_first_unused_index] = 0;
|
|
}
|
|
|
|
void
|
|
SML_PopTag(SML_StyleAndTagStack *self, char *name)
|
|
{
|
|
SML_PopTagByIndex(self, name, 0);
|
|
}
|
|
|
|
/* the zero'th index is the top of the stack
|
|
*/
|
|
TagAndStyleAssoc *
|
|
sml_get_index(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
int32 real_index = (self->tag_stack_first_unused_index-1) - index;
|
|
|
|
if(real_index < 0)
|
|
return NULL;
|
|
|
|
return(self->tag_stack[real_index]);
|
|
}
|
|
|
|
/* the zero'th index is the bottom of the stack
|
|
*/
|
|
TagAndStyleAssoc *
|
|
sml_get_reverse_index(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
if(index >= self->tag_stack_first_unused_index)
|
|
return NULL;
|
|
|
|
return(self->tag_stack[index]);
|
|
}
|
|
|
|
/* the zero'th index is the top of the stack
|
|
*/
|
|
TagStruct *
|
|
SML_GetTagByIndex(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
TagAndStyleAssoc *assoc = sml_get_index(self, index);
|
|
|
|
if(!assoc)
|
|
return NULL;
|
|
|
|
return(assoc->tag);
|
|
}
|
|
|
|
/* the zero'th index is the bottom of the stack
|
|
*/
|
|
TagStruct *
|
|
SML_GetTagByReverseIndex(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
TagAndStyleAssoc *assoc = sml_get_reverse_index(self, index);
|
|
|
|
if(!assoc)
|
|
return NULL;
|
|
|
|
return(assoc->tag);
|
|
}
|
|
|
|
/* the zero'th index is the top of the stack
|
|
*/
|
|
StyleStruct *
|
|
SML_GetStyleByIndex(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
TagAndStyleAssoc *assoc = sml_get_index(self, index);
|
|
|
|
if(!assoc)
|
|
return NULL;
|
|
|
|
return(assoc->style);
|
|
}
|
|
|
|
/* the zero'th index is the bottom of the stack
|
|
*/
|
|
StyleStruct *
|
|
SML_GetStyleByReverseIndex(SML_StyleAndTagStack *self, int32 index)
|
|
{
|
|
TagAndStyleAssoc *assoc = sml_get_reverse_index(self, index);
|
|
|
|
if(!assoc)
|
|
return NULL;
|
|
|
|
return(assoc->style);
|
|
}
|
|
|
|
void
|
|
SML_Delete(SML_StyleAndTagStack *self)
|
|
{
|
|
self->refcount--;
|
|
|
|
if(self->refcount > 0)
|
|
return;
|
|
|
|
sml_free_stack(self);
|
|
|
|
XP_FREE(self);
|
|
}
|
|
|
|
void
|
|
SML_Purge(SML_StyleAndTagStack *self)
|
|
{
|
|
sml_free_stack(self);
|
|
}
|
|
|
|
void
|
|
SML_Init(SML_StyleAndTagStack *self, MWContext *context)
|
|
{
|
|
self->context = context;
|
|
}
|
|
|
|
/* static vtable */
|
|
const StyleAndTagStackInterface StyleAndTagStack_interface = {
|
|
|
|
(void (*)(StyleAndTagStack *self, MWContext *context))
|
|
SML_Init,
|
|
(TagStruct * (*)(StyleAndTagStack *self, char *name, char *class_name, char *id))
|
|
SML_NewTagStruct,
|
|
(void (*)(StyleAndTagStack *self, TagStruct *tag))
|
|
SML_FreeTagStruct,
|
|
(PushTagStatus (*)(StyleAndTagStack *self, char *name, char *class_name, char *id))
|
|
SML_PushTag,
|
|
(void (*)(StyleAndTagStack *self, char *name))
|
|
SML_PopTag,
|
|
(void (*)(StyleAndTagStack *self, char *name, int32 index))
|
|
SML_PopTagByIndex,
|
|
(TagStruct * (*)(StyleAndTagStack *self, int32 index))
|
|
SML_GetTagByIndex,
|
|
(TagStruct * (*)(StyleAndTagStack *self, int32 index))
|
|
SML_GetTagByReverseIndex,
|
|
(StyleStruct * (*)(StyleAndTagStack *self, int32 index))
|
|
SML_GetStyleByIndex,
|
|
(StyleStruct * (*)(StyleAndTagStack *self, int32 index))
|
|
SML_GetStyleByReverseIndex,
|
|
(StyleStruct * (*)(StyleAndTagStack *self))
|
|
SML_Delete,
|
|
(StyleStruct * (*)(StyleAndTagStack *self))
|
|
SML_Purge,
|
|
(void (*)(StyleAndTagStack *self, XP_Bool on_flag))
|
|
SML_SetSaveOn,
|
|
(XP_Bool (*)(StyleAndTagStack *self))
|
|
SML_IsSaveOn
|
|
};
|
|
|
|
StyleAndTagStack *
|
|
SML_StyleStack_Factory_Create(void)
|
|
{
|
|
/* initializer */
|
|
SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)XP_CALLOC(1, sizeof(SML_StyleAndTagStack));
|
|
|
|
if(!self)
|
|
return NULL;
|
|
|
|
self->vtable = (void*)&StyleAndTagStack_interface;
|
|
self->refcount = 1;
|
|
|
|
return (StyleAndTagStack*)self;
|
|
}
|
|
|
|
void
|
|
SML_SetObjectRefs(StyleAndTagStack *styleStack,
|
|
StyleObject *tags,
|
|
StyleObject *classes,
|
|
StyleObject *ids)
|
|
{
|
|
SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)styleStack;
|
|
|
|
self->tags = tags;
|
|
self->classes = classes;
|
|
self->ids = ids;
|
|
}
|
|
|
|
/*
|
|
* Helper routine to assemble the JSSContext
|
|
*/
|
|
void
|
|
sml_GetJSSContext(StyleAndTagStack *styleStack, JSSContext *jc)
|
|
{
|
|
SML_StyleAndTagStack * self = (SML_StyleAndTagStack*)styleStack;
|
|
|
|
/* Lookup document.tags */
|
|
jc->tags = self->tags;
|
|
|
|
/* Lookup document.classes */
|
|
jc->classes = self->classes;
|
|
|
|
/* Lookup up document.ids */
|
|
jc->ids = self->ids;
|
|
}
|
|
|
|
#ifdef TEST_STYLESTACK
|
|
|
|
typedef struct {
|
|
char name[100];
|
|
char class_name[100];
|
|
char id[100];
|
|
} test_struct;
|
|
|
|
test_struct test_table[] = {
|
|
|
|
{"h1", "class1", "id1"},
|
|
{"h2", "class2", "id2"},
|
|
{"h3", "class3", "id3"},
|
|
{"h4", "class4", "id4"},
|
|
|
|
{"h5", "", "id1"},
|
|
{"h6", "", "id2"},
|
|
{"h7", "", "id3"},
|
|
{"h8", "", "id4"},
|
|
|
|
{"h9", "class9", ""},
|
|
{"h10", "class10", ""},
|
|
{"h11", "class11", ""},
|
|
{"h12", "class12", ""},
|
|
|
|
{"h13", "", ""},
|
|
{"h14", "", ""},
|
|
{"h15", "", ""},
|
|
{"h16", "", ""},
|
|
|
|
{"", "", ""}
|
|
};
|
|
|
|
void
|
|
test_values(StyleAndTagStack * h)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; *test_table[i].name; i++)
|
|
; /* null body */
|
|
|
|
for(i--; i >= 0; i--)
|
|
{
|
|
test_struct *ts = &test_table[i];
|
|
|
|
TagStruct *tag = STYLESTACK_GetTagByIndex(h, 0);
|
|
|
|
if(strcmp(tag->name, ts->name))
|
|
printf("Error: names do not match: %s:%s\n", tag->name, ts->name);
|
|
else if(*ts->class && strcmp(tag->class, ts->class))
|
|
printf("Error: class names do not match: %s:%s\n", tag->class_name, ts->class_name);
|
|
else if(*ts->id && strcmp(tag->id, ts->id))
|
|
printf("Error: id names do not match: %s:%s\n", tag->id, ts->id);
|
|
else
|
|
printf("Success: retreival successful: %s\n", tag->name);
|
|
|
|
STYLESTACK_PopTag(h, ts->name);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
StyleAndTagStack * h;
|
|
StyleStruct *style;
|
|
|
|
|
|
h = SML_StyleStack_Factory_Create();
|
|
|
|
if(!h)
|
|
exit(1);
|
|
|
|
for(i=0; *test_table[i].name; i++)
|
|
{
|
|
test_struct *ts = &test_table[i];
|
|
|
|
style = STYLESTACK_PushTag(h, ts->name, ts->class_name, ts->id);
|
|
|
|
if(!style)
|
|
printf("stack (or malloc) error");
|
|
}
|
|
|
|
test_values(h);
|
|
|
|
printf("\nAll tests complete\n");
|
|
|
|
exit(0);
|
|
}
|
|
|
|
#endif /* TEST_STYLESTACK */
|