зеркало из https://github.com/mozilla/pjs.git
967 строки
26 KiB
C
967 строки
26 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.
|
||
|
*/
|
||
|
|
||
|
/* CSStoJS tranlation code */
|
||
|
|
||
|
#include "xp.h"
|
||
|
#include "css.h"
|
||
|
#include "cssI.h"
|
||
|
|
||
|
/* ALLOW_IMPORT
|
||
|
* Switch allows or stops the translation of @import rules.
|
||
|
*/
|
||
|
#define ALLOW_IMPORT 0
|
||
|
|
||
|
/* ALLOW_FONTDEF
|
||
|
* Switch allows or stops the translation of @fontdef rules.
|
||
|
*/
|
||
|
#define ALLOW_FONTDEF 1
|
||
|
|
||
|
/* ALLOW_PRIORITY
|
||
|
* Switch allows or stops the translation of "!important" in the value.
|
||
|
*/
|
||
|
#define ALLOW_PRIORITY 0
|
||
|
|
||
|
/* ALLOW_PSEUDO_ELEMENTS
|
||
|
* Switch allows or prevents the translation of rules with pseudo-elements
|
||
|
* :first-line and :first-letter in the selector.
|
||
|
*/
|
||
|
#define ALLOW_PSEUDO_ELEMENTS 0
|
||
|
|
||
|
/* ALLOW_ACTIVE_PSEUDO_CLASS
|
||
|
* Switch allows or prevents the translation of rules with pseudo-class
|
||
|
* :active in the selector.
|
||
|
*/
|
||
|
#define ALLOW_ACTIVE_PSEUDO_CLASS 0
|
||
|
|
||
|
#define STYLE_INITIAL_BUFSIZ 1024
|
||
|
#define STYLE_INCR_BUFSIZ 1024
|
||
|
|
||
|
/* property type values */
|
||
|
#define CSS_PROPERTY_TYPE_ASSIGNMENT (1)
|
||
|
#define CSS_PROPERTY_TYPE_CALL (2)
|
||
|
/* pseudo class type values */
|
||
|
#define CSS_PSEUDO_CLASS_NONE (0)
|
||
|
#define CSS_PSEUDO_CLASS_LINK (1)
|
||
|
#define CSS_PSEUDO_CLASS_ACTIVE (2)
|
||
|
#define CSS_PSEUDO_CLASS_VISITED (3)
|
||
|
|
||
|
typedef struct {
|
||
|
char * buffer;
|
||
|
int32 buffer_size; /* size of buffer in bytes */
|
||
|
int32 buffer_count; /* bytes used */
|
||
|
int32 prior_count; /* buffer_count at beginning of rule */
|
||
|
int32 pseudo_class_state; /* holds temporary state during translation */
|
||
|
XP_Bool ignore_rule; /* during translation, may decide to ignore a rule */
|
||
|
} *StyleBuffer, StyleBufferRec;
|
||
|
|
||
|
XP_BEGIN_PROTOS
|
||
|
|
||
|
static XP_Bool ConvertStyleSheet(css_node stylesheet, StyleBuffer sb);
|
||
|
static void ConvertRule(css_node rule, StyleBuffer sb);
|
||
|
static void ConvertSelector(css_node selector, css_node pseudo_element,
|
||
|
StyleBuffer sb);
|
||
|
static void ConvertContextualSelector(css_node selector, css_node pseudo_element,
|
||
|
StyleBuffer sb);
|
||
|
static void ConvertSingleSelector(css_node simple, css_node pseudo_element,
|
||
|
StyleBuffer sb);
|
||
|
static void ConvertDeclaration(css_node declaration_list, StyleBuffer sb);
|
||
|
static void ConvertProperty(const char * css1_property, StyleBuffer sb,
|
||
|
int * property_type_return);
|
||
|
static void ConvertValue(css_node value, StyleBuffer sb, int property_type);
|
||
|
static void ConvertURLValue(char * url_value, StyleBuffer sb);
|
||
|
static void StripURLValue(char ** url_return, int * url_length_return);
|
||
|
static void ConvertStringValue(char * string_value, StyleBuffer sb);
|
||
|
static void StyleBufferWrite(char * str, int len, StyleBuffer sb);
|
||
|
static void AnteRule(StyleBuffer sb);
|
||
|
static void IgnoreRule(StyleBuffer sb);
|
||
|
static void PostRule(StyleBuffer sb);
|
||
|
static void SaltPseudoClass(const char * pseudo_class, StyleBuffer sb);
|
||
|
static int32 GetPseudoClass(StyleBuffer sb);
|
||
|
|
||
|
#if (ALLOW_IMPORT || ALLOW_FONTDEF)
|
||
|
static void ConvertToLink(css_node ruleset, int rel_type, StyleBuffer sb);
|
||
|
static void EchoCSS(char * src, int32 src_count, StyleBuffer sb);
|
||
|
#endif
|
||
|
|
||
|
XP_END_PROTOS
|
||
|
|
||
|
static char * input_buffer;
|
||
|
static int32 input_buffer_count; /* bytes used, not including NULL */
|
||
|
static int32 input_buffer_index;
|
||
|
|
||
|
/* result and max_to_read are created and passed as int, not int32, on Win16. */
|
||
|
void css_GetBuf(char * buf, int *result, int max_to_read)
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
if (input_buffer) {
|
||
|
|
||
|
while ((i < max_to_read) && (input_buffer_count > input_buffer_index))
|
||
|
buf[i++] = input_buffer[input_buffer_index++];
|
||
|
|
||
|
if (0 == i) {
|
||
|
input_buffer = NULL;
|
||
|
input_buffer_count = input_buffer_index = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*result = i;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Java and JavaScript */
|
||
|
static const char * reserved_words[] = {
|
||
|
"abstract",
|
||
|
"boolean",
|
||
|
"break",
|
||
|
"byte",
|
||
|
"case",
|
||
|
"catch",
|
||
|
"char",
|
||
|
"class",
|
||
|
"const",
|
||
|
"continue",
|
||
|
"default",
|
||
|
"delete",
|
||
|
"do",
|
||
|
"double",
|
||
|
"else",
|
||
|
"extends",
|
||
|
"false",
|
||
|
"final",
|
||
|
"finally",
|
||
|
"float",
|
||
|
"for",
|
||
|
"function",
|
||
|
"goto",
|
||
|
"if",
|
||
|
"implements",
|
||
|
"import",
|
||
|
"in",
|
||
|
"instanceof",
|
||
|
"int",
|
||
|
"interface",
|
||
|
"long",
|
||
|
"native",
|
||
|
"new",
|
||
|
"null",
|
||
|
"package",
|
||
|
"private",
|
||
|
"protected",
|
||
|
"public",
|
||
|
"return",
|
||
|
"short",
|
||
|
"static",
|
||
|
"super",
|
||
|
"switch",
|
||
|
"synchronized",
|
||
|
"this",
|
||
|
"throw",
|
||
|
"throws",
|
||
|
"transient",
|
||
|
"true",
|
||
|
"try",
|
||
|
"typeof",
|
||
|
"var",
|
||
|
"void",
|
||
|
"volatile",
|
||
|
"while",
|
||
|
"with"
|
||
|
};
|
||
|
|
||
|
static XP_Bool IsReservedWord(const char * css_name)
|
||
|
{
|
||
|
int lower = 0;
|
||
|
int upper = (sizeof(reserved_words) / sizeof(char *)) - 1;
|
||
|
int x;
|
||
|
int32 result;
|
||
|
|
||
|
while (upper >= lower) {
|
||
|
x = (lower + upper) / 2;
|
||
|
result = XP_STRCMP(css_name, reserved_words[x]);
|
||
|
if (result < 0)
|
||
|
upper = x - 1;
|
||
|
else if (result > 0)
|
||
|
lower = x + 1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* CSS_ConvertToJSCompatibleName
|
||
|
* converts characters which are illegal in JavaScript names.
|
||
|
*
|
||
|
* Anne-Marie ==> Anne_Marie "-" (dash) always converts to "_"
|
||
|
* 5NewportRd ==> _5NewportRd 1st character a digit prepend "_"
|
||
|
* Convert lower to upper case when second parameter is true.
|
||
|
* When second parameter is false, prepend "_" to reserved words.
|
||
|
* Returns NULL on error.
|
||
|
*/
|
||
|
PUBLIC char *
|
||
|
CSS_ConvertToJSCompatibleName(char *css_name, XP_Bool uppercase_it)
|
||
|
{
|
||
|
int32 len;
|
||
|
char *new_name;
|
||
|
char *cp;
|
||
|
XP_Bool reserved = 0;
|
||
|
|
||
|
XP_ASSERT(css_name);
|
||
|
|
||
|
if (!css_name)
|
||
|
return NULL;
|
||
|
|
||
|
len = XP_STRLEN(css_name);
|
||
|
if (! uppercase_it)
|
||
|
reserved = IsReservedWord(css_name);
|
||
|
if (reserved || XP_IS_DIGIT(*css_name))
|
||
|
len++;
|
||
|
|
||
|
cp = new_name = XP_ALLOC((len+1) * sizeof(char));
|
||
|
if (!new_name)
|
||
|
return NULL;
|
||
|
|
||
|
if (reserved || XP_IS_DIGIT(*css_name)) {
|
||
|
*cp = '_';
|
||
|
cp++;
|
||
|
}
|
||
|
|
||
|
for(; *css_name; css_name++, cp++)
|
||
|
if (*css_name == '-')
|
||
|
*cp = '_';
|
||
|
else
|
||
|
*cp = uppercase_it ? (XP_TO_UPPER(*css_name)) : *css_name;
|
||
|
|
||
|
*cp = '\0';
|
||
|
|
||
|
return new_name;
|
||
|
}
|
||
|
|
||
|
|
||
|
PUBLIC
|
||
|
void CSS_ConvertToJS(char * src, int32 src_count, char ** dst, int32 * dst_count)
|
||
|
{
|
||
|
StyleBuffer sb;
|
||
|
XP_Bool translated;
|
||
|
|
||
|
*dst = NULL;
|
||
|
*dst_count = 0;
|
||
|
|
||
|
if (NULL == src || 0 == src_count)
|
||
|
return;
|
||
|
|
||
|
input_buffer = src;
|
||
|
input_buffer_count = src_count;
|
||
|
input_buffer_index = 0;
|
||
|
|
||
|
css_tree_root = NULL;
|
||
|
if (css_parse() != 0)
|
||
|
return;
|
||
|
|
||
|
sb = XP_NEW_ZAP(StyleBufferRec);
|
||
|
if (sb == NULL)
|
||
|
return;
|
||
|
|
||
|
translated = ConvertStyleSheet(css_tree_root, sb);
|
||
|
css_FreeNode(css_tree_root);
|
||
|
#if ALLOW_IMPORT
|
||
|
if (! translated)
|
||
|
EchoCSS(src, src_count, sb);
|
||
|
#endif
|
||
|
|
||
|
/* The caller should free *dst. */
|
||
|
*dst = sb->buffer;
|
||
|
*dst_count = sb->buffer_count;
|
||
|
XP_DELETE(sb);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Release all subtree and the node */
|
||
|
void css_FreeNode(css_node vp)
|
||
|
{
|
||
|
if (vp) {
|
||
|
css_FreeNode(vp->left);
|
||
|
css_FreeNode(vp->right);
|
||
|
if (vp->string)
|
||
|
XP_FREE(vp->string);
|
||
|
XP_DELETE(vp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static XP_Bool ConvertStyleSheet(css_node stylesheet, StyleBuffer sb)
|
||
|
{
|
||
|
|
||
|
#if (ALLOW_IMPORT || ALLOW_FONTDEF)
|
||
|
XP_Bool translate_rules = 1;
|
||
|
|
||
|
/* CSS1 requires @import productions occurring after
|
||
|
* ruleset productions to be ignored. This is implemented
|
||
|
* here. The restriction is not enforced by the parser.
|
||
|
*/
|
||
|
for (; stylesheet && (stylesheet->node_id != NODE_RULESET_LIST);
|
||
|
stylesheet = stylesheet->left) {
|
||
|
#if ALLOW_IMPORT
|
||
|
if (NODE_IMPORT_LIST == stylesheet->node_id) {
|
||
|
ConvertToLink(stylesheet->right, stylesheet->node_id, sb);
|
||
|
translate_rules = 0;
|
||
|
}
|
||
|
#endif
|
||
|
#if ALLOW_FONTDEF
|
||
|
if (NODE_FONTDEF_LIST == stylesheet->node_id) {
|
||
|
ConvertToLink(stylesheet->right, stylesheet->node_id, sb);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#if (ALLOW_IMPORT)
|
||
|
if (! translate_rules)
|
||
|
return 0;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
for (; stylesheet; stylesheet = stylesheet->left) {
|
||
|
if (stylesheet->node_id == NODE_RULESET_LIST)
|
||
|
ConvertRule(stylesheet->right, sb);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Generate the JavaScript corresponding to one CSS1 rule. */
|
||
|
static void ConvertRule(css_node rule, StyleBuffer sb)
|
||
|
{
|
||
|
css_node selector_list, selector;
|
||
|
css_node declaration_list;
|
||
|
|
||
|
if (! rule || rule->node_id != NODE_RULESET)
|
||
|
return;
|
||
|
|
||
|
for (selector_list = rule->left; selector_list;
|
||
|
selector_list = selector_list->left) {
|
||
|
|
||
|
selector = selector_list->right;
|
||
|
if (! selector)
|
||
|
continue;
|
||
|
|
||
|
for (declaration_list = rule->right; declaration_list;
|
||
|
declaration_list = declaration_list->left) {
|
||
|
|
||
|
if (declaration_list->right) {
|
||
|
AnteRule(sb);
|
||
|
ConvertSelector(selector->left, selector->right, sb);
|
||
|
ConvertDeclaration(declaration_list->right, sb);
|
||
|
PostRule(sb);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void AnteRule(StyleBuffer sb)
|
||
|
{
|
||
|
sb->prior_count = sb->buffer_count;
|
||
|
sb->pseudo_class_state = CSS_PSEUDO_CLASS_NONE;
|
||
|
}
|
||
|
|
||
|
static void PostRule(StyleBuffer sb)
|
||
|
{
|
||
|
if (sb->ignore_rule) {
|
||
|
sb->buffer_count = sb->prior_count;
|
||
|
sb->ignore_rule = 0;
|
||
|
if (0 == sb->buffer_count)
|
||
|
sb->buffer_count = 1;
|
||
|
sb->buffer[sb->buffer_count-1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void StyleBufferWrite(char * str, int len, StyleBuffer sb)
|
||
|
{
|
||
|
|
||
|
if (str == NULL)
|
||
|
return;
|
||
|
|
||
|
if (len <= 0) {
|
||
|
len = strlen(str);
|
||
|
if (len <= 0)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (sb->buffer == NULL) {
|
||
|
sb->buffer = (char *) XP_ALLOC(STYLE_INITIAL_BUFSIZ);
|
||
|
if (sb->buffer) {
|
||
|
sb->buffer_size = STYLE_INITIAL_BUFSIZ;
|
||
|
*sb->buffer = '\0';
|
||
|
sb->buffer_count = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sb->buffer_count + len > sb->buffer_size) {
|
||
|
sb->buffer = (char *)
|
||
|
XP_REALLOC(sb->buffer, sb->buffer_size + STYLE_INCR_BUFSIZ);
|
||
|
if (sb->buffer) {
|
||
|
sb->buffer_size += STYLE_INCR_BUFSIZ;
|
||
|
} else {
|
||
|
sb->buffer_size = sb->buffer_count = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sb->buffer_count + len <= sb->buffer_size) {
|
||
|
(void) strncpy(sb->buffer + sb->buffer_count - 1, str, len);
|
||
|
sb->buffer_count += len;
|
||
|
sb->buffer[sb->buffer_count-1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertSelector(css_node selector, css_node pseudo_element,
|
||
|
StyleBuffer sb)
|
||
|
{
|
||
|
if (selector->node_id == NODE_SELECTOR_CONTEXTUAL) {
|
||
|
StyleBufferWrite("contextual(", 11, sb);
|
||
|
ConvertContextualSelector(selector, pseudo_element, sb);
|
||
|
StyleBufferWrite(")", 1, sb);
|
||
|
} else {
|
||
|
ConvertSingleSelector(selector, pseudo_element, sb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertContextualSelector(css_node selector,
|
||
|
css_node pseudo_element, StyleBuffer sb)
|
||
|
{
|
||
|
if (selector->left) {
|
||
|
/* pseudo_elements may be present only on the final context object */
|
||
|
ConvertContextualSelector(selector->left, NULL, sb);
|
||
|
StyleBufferWrite(", ", 2, sb);
|
||
|
}
|
||
|
|
||
|
ConvertSingleSelector(selector->right, pseudo_element, sb);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertSingleSelector(css_node selector, css_node pseudo_element,
|
||
|
StyleBuffer sb)
|
||
|
{
|
||
|
char * str1;
|
||
|
char * str2;
|
||
|
|
||
|
if (! selector)
|
||
|
return;
|
||
|
|
||
|
if (selector->node_id == NODE_WILD) {
|
||
|
/* CSS1 Section 7.1 states: "A ruleset that [has] a selector
|
||
|
* string that is not valid CSS1 is skipped." (sic)
|
||
|
*/
|
||
|
IgnoreRule(sb);
|
||
|
}
|
||
|
|
||
|
if (! selector->left)
|
||
|
return;
|
||
|
|
||
|
str1 = selector->left->string;
|
||
|
str2 = selector->right ? selector->right->string : NULL;
|
||
|
|
||
|
switch (selector->node_id) {
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_NAME_ONLY:
|
||
|
|
||
|
/* "document.tags.<TAG>" */
|
||
|
StyleBufferWrite("document.tags.", 14, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
break;
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_DOT_AND_CLASS:
|
||
|
|
||
|
/* "document.classes.<class>.all" */
|
||
|
StyleBufferWrite("document.classes.", 17, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
StyleBufferWrite(".all", 4, sb);
|
||
|
break;
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_ID_SELECTOR:
|
||
|
|
||
|
/* "document.ids.<id>" */
|
||
|
StyleBufferWrite("document.ids.", 13, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
break;
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_NAME_AND_CLASS:
|
||
|
|
||
|
/* "document.classes.<class>.<TAG>" */
|
||
|
StyleBufferWrite("document.classes.", 17, sb);
|
||
|
str2 = CSS_ConvertToJSCompatibleName(str2, FALSE);
|
||
|
StyleBufferWrite(str2, 0, sb);
|
||
|
XP_FREE(str2);
|
||
|
StyleBufferWrite(".", 1, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
break;
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_NAME_PSEUDO_CLASS:
|
||
|
|
||
|
/* "document.tags.<TAG> */
|
||
|
/* By design, prepend the pseudoclass name to the property
|
||
|
* name when the property is 'color'; otherwise, ignore the
|
||
|
* pseudoclass name.
|
||
|
*/
|
||
|
StyleBufferWrite("document.tags.", 14, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
SaltPseudoClass(str2, sb);
|
||
|
break;
|
||
|
|
||
|
case NODE_SIMPLE_SELECTOR_NAME_CLASS_PSEUDO_CLASS:
|
||
|
|
||
|
/* "document.classes.<class>.<TAG>" */
|
||
|
/* By design, prepend the pseudoclass name to the property
|
||
|
* name when the property is 'color'; otherwise, ignore the
|
||
|
* pseudoclass name.
|
||
|
*/
|
||
|
StyleBufferWrite("document.classes.", 17, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
StyleBufferWrite(".", 1, sb);
|
||
|
str1 = CSS_ConvertToJSCompatibleName(selector->string, TRUE);
|
||
|
StyleBufferWrite(str1, 0, sb);
|
||
|
XP_FREE(str1);
|
||
|
SaltPseudoClass(str2, sb);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pseudo_element) {
|
||
|
#if ! ALLOW_PSEUDO_ELEMENTS
|
||
|
IgnoreRule(sb);
|
||
|
#endif
|
||
|
StyleBufferWrite(".", 1, sb);
|
||
|
ConvertProperty(pseudo_element->string, sb, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void IgnoreRule(StyleBuffer sb)
|
||
|
{
|
||
|
sb->ignore_rule = 1;
|
||
|
}
|
||
|
|
||
|
static void SaltPseudoClass(const char * pseudo_class, StyleBuffer sb)
|
||
|
{
|
||
|
if (! pseudo_class)
|
||
|
return;
|
||
|
|
||
|
/* The parser always gives these strings in lower case. */
|
||
|
if (0 == XP_STRCMP(pseudo_class, "link"))
|
||
|
sb->pseudo_class_state = CSS_PSEUDO_CLASS_LINK;
|
||
|
else if (0 == XP_STRCMP(pseudo_class, "visited"))
|
||
|
sb->pseudo_class_state = CSS_PSEUDO_CLASS_VISITED;
|
||
|
else if (0 == XP_STRCMP(pseudo_class, "active")) {
|
||
|
#if (! ALLOW_ACTIVE_PSEUDO_CLASS)
|
||
|
IgnoreRule(sb);
|
||
|
#endif
|
||
|
sb->pseudo_class_state = CSS_PSEUDO_CLASS_ACTIVE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int32 GetPseudoClass(StyleBuffer sb)
|
||
|
{
|
||
|
return sb->pseudo_class_state;
|
||
|
}
|
||
|
|
||
|
/* Pseudo-elements as well as property names are converted by this. */
|
||
|
static void ConvertProperty(const char * css1_property, StyleBuffer sb,
|
||
|
int * property_type_return)
|
||
|
{
|
||
|
XP_Bool upper;
|
||
|
char ch[2];
|
||
|
int32 pseudo_class;
|
||
|
|
||
|
ch[1] = '\0';
|
||
|
|
||
|
if (property_type_return)
|
||
|
*property_type_return = CSS_PROPERTY_TYPE_ASSIGNMENT;
|
||
|
|
||
|
/* The CSS1 float property is translated to align because float is
|
||
|
* a reserved word in JavaScript.
|
||
|
*/
|
||
|
if (0 == XP_STRCASECMP(css1_property, "float"))
|
||
|
StyleBufferWrite("align", 5, sb);
|
||
|
else {
|
||
|
if (property_type_return) {
|
||
|
if ( (0 == XP_STRCASECMP(css1_property, "margin"))
|
||
|
|| (0 == XP_STRCASECMP(css1_property, "padding"))
|
||
|
|| (0 == XP_STRCASECMP(css1_property, "border-width")))
|
||
|
*property_type_return = CSS_PROPERTY_TYPE_CALL;
|
||
|
}
|
||
|
|
||
|
pseudo_class = GetPseudoClass(sb);
|
||
|
|
||
|
if (0 == XP_STRCASECMP(css1_property, "margin"))
|
||
|
StyleBufferWrite("margins", 7, sb);
|
||
|
else if (0 == XP_STRCASECMP(css1_property, "padding"))
|
||
|
StyleBufferWrite("paddings", 8, sb);
|
||
|
else if (0 == XP_STRCASECMP(css1_property, "border-width"))
|
||
|
StyleBufferWrite("borderWidths", 12, sb);
|
||
|
else if ((CSS_PSEUDO_CLASS_NONE != pseudo_class)
|
||
|
&& (0 == XP_STRCASECMP(css1_property, "color"))) {
|
||
|
switch (pseudo_class) {
|
||
|
case CSS_PSEUDO_CLASS_LINK:
|
||
|
default:
|
||
|
StyleBufferWrite("linkColor", 9, sb);
|
||
|
break;
|
||
|
case CSS_PSEUDO_CLASS_ACTIVE:
|
||
|
StyleBufferWrite("activeColor", 11, sb);
|
||
|
break;
|
||
|
case CSS_PSEUDO_CLASS_VISITED:
|
||
|
StyleBufferWrite("visitedColor", 12, sb);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (upper=0; *css1_property; css1_property++) {
|
||
|
|
||
|
if (*css1_property == '-') {
|
||
|
upper = 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ch[0] = (upper ? toupper(*css1_property)
|
||
|
: tolower(*css1_property));
|
||
|
upper = 0;
|
||
|
StyleBufferWrite(ch, 1, sb);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertDeclaration(css_node declaration, StyleBuffer sb)
|
||
|
{
|
||
|
int property_type;
|
||
|
|
||
|
if (declaration &&
|
||
|
(declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR ||
|
||
|
declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR_PRIO)) {
|
||
|
StyleBufferWrite(".", 1, sb);
|
||
|
ConvertProperty(declaration->left->string, sb, &property_type);
|
||
|
ConvertValue(declaration->right, sb, property_type);
|
||
|
#if ALLOW_PRIORITY
|
||
|
if (declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR_PRIO)
|
||
|
StyleBufferWrite(" !important", 11, sb);
|
||
|
#endif
|
||
|
StyleBufferWrite("\n", 1, sb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertValue(css_node value, StyleBuffer sb, int property_type)
|
||
|
{
|
||
|
css_node term;
|
||
|
XP_Bool comma;
|
||
|
|
||
|
|
||
|
if (CSS_PROPERTY_TYPE_ASSIGNMENT == property_type)
|
||
|
StyleBufferWrite(" = \"", 4, sb);
|
||
|
else
|
||
|
StyleBufferWrite("(", 1, sb);
|
||
|
|
||
|
for (comma=FALSE; value; value=value->left, comma = TRUE) {
|
||
|
|
||
|
if (CSS_PROPERTY_TYPE_CALL == property_type && comma)
|
||
|
StyleBufferWrite(",", 1, sb);
|
||
|
|
||
|
if (value->node_id == NODE_EMPTY_OP)
|
||
|
StyleBufferWrite(" ", 1, sb);
|
||
|
|
||
|
if (CSS_PROPERTY_TYPE_CALL == property_type)
|
||
|
StyleBufferWrite("\"", 1, sb);
|
||
|
|
||
|
if (value->node_id == NODE_EXPR_OP)
|
||
|
StyleBufferWrite(value->string, 0, sb);
|
||
|
|
||
|
term = value->right;
|
||
|
if (term) {
|
||
|
if (term->node_id == NODE_STRING)
|
||
|
ConvertStringValue(term->string, sb);
|
||
|
else if (NODE_URL == term->node_id)
|
||
|
ConvertURLValue(term->string, sb);
|
||
|
else {
|
||
|
StyleBufferWrite(term->string, 0, sb);
|
||
|
if (term->node_id == NODE_UNARY_OP)
|
||
|
StyleBufferWrite(term->left->string, 0, sb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (CSS_PROPERTY_TYPE_CALL == property_type)
|
||
|
StyleBufferWrite("\"", 1, sb);
|
||
|
}
|
||
|
|
||
|
if (CSS_PROPERTY_TYPE_ASSIGNMENT == property_type)
|
||
|
StyleBufferWrite("\"", 1, sb);
|
||
|
else
|
||
|
StyleBufferWrite(")", 1, sb);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ConvertStringValue(char * string_value, StyleBuffer sb)
|
||
|
{
|
||
|
for (; *string_value; string_value++) {
|
||
|
if (*string_value == '"')
|
||
|
StyleBufferWrite("\\", 1, sb);
|
||
|
StyleBufferWrite(string_value, 1, sb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void StripURLValue(char ** url_return, int * url_length_return)
|
||
|
{
|
||
|
char * ptr;
|
||
|
int len;
|
||
|
|
||
|
if (! url_return || ! *url_return ||
|
||
|
! url_length_return || ! *url_length_return)
|
||
|
return;
|
||
|
|
||
|
ptr = *url_return;
|
||
|
len = *url_length_return;
|
||
|
|
||
|
/* Skip leading whitespace */
|
||
|
while (len > 0 && isspace(*ptr)) {
|
||
|
ptr++;
|
||
|
len--;
|
||
|
}
|
||
|
|
||
|
/* Skip trailing whitespace */
|
||
|
while (len > 0 && isspace(*(ptr + len - 1)))
|
||
|
len--;
|
||
|
|
||
|
/* Skip enclosing quotation marks */
|
||
|
if (('"' == *ptr || '\'' == *ptr ) &&
|
||
|
(len > 1) &&
|
||
|
(*ptr == *(ptr + len - 1))) {
|
||
|
ptr++;
|
||
|
len -= 2;
|
||
|
}
|
||
|
|
||
|
*url_return = ptr;
|
||
|
*url_length_return = len;
|
||
|
}
|
||
|
|
||
|
static void ConvertURLValue(char * url_value, StyleBuffer sb)
|
||
|
{
|
||
|
char * ptr = url_value;
|
||
|
int len;
|
||
|
|
||
|
len = strlen(ptr);
|
||
|
if (len < 6)
|
||
|
return;
|
||
|
|
||
|
/* Don't count the 5 characters for 'url()' */
|
||
|
len -= 5;
|
||
|
StyleBufferWrite("url(", 4, sb);
|
||
|
ptr += 4;
|
||
|
|
||
|
StripURLValue(&ptr, &len);
|
||
|
|
||
|
if (len > 0)
|
||
|
StyleBufferWrite(ptr, len, sb);
|
||
|
|
||
|
StyleBufferWrite(")", 1, sb);
|
||
|
}
|
||
|
|
||
|
#if (ALLOW_IMPORT || ALLOW_FONTDEF)
|
||
|
|
||
|
static char * open_call = "document.write('";
|
||
|
static char * close_call = "');\n";
|
||
|
|
||
|
static void ConvertToLink(css_node rule, int rel_type, StyleBuffer sb)
|
||
|
{
|
||
|
int len;
|
||
|
char * ptr;
|
||
|
char ch;
|
||
|
char hexbuf[4];
|
||
|
|
||
|
if (rule && rule->string) {
|
||
|
if ((NODE_IMPORT_URL == rule->node_id) ||
|
||
|
(NODE_IMPORT_STRING == rule->node_id)) {
|
||
|
|
||
|
StyleBufferWrite(open_call, 0, sb);
|
||
|
|
||
|
if (NODE_IMPORT_LIST == rel_type)
|
||
|
StyleBufferWrite("<LINK REL=stylesheet ", 21, sb);
|
||
|
else
|
||
|
StyleBufferWrite("<LINK REL=fontdef ", 18, sb);
|
||
|
|
||
|
/* Copy the url */
|
||
|
len = XP_STRLEN(rule->string);
|
||
|
ptr = NULL;
|
||
|
if ((NODE_IMPORT_URL == rule->node_id) && (len > 5)) {
|
||
|
/* Copy the characters inside the parens */
|
||
|
ptr = rule->string + 4;
|
||
|
len -= 5;
|
||
|
StripURLValue(&ptr, &len);
|
||
|
if (len <= 0)
|
||
|
ptr = NULL;
|
||
|
} else {
|
||
|
/* Copy the characters inside the quotes */
|
||
|
ptr = rule->string + 1;
|
||
|
len -= 2;
|
||
|
}
|
||
|
if (NULL != ptr) {
|
||
|
if (NODE_IMPORT_LIST == rel_type)
|
||
|
StyleBufferWrite("HREF=", 5, sb);
|
||
|
else
|
||
|
StyleBufferWrite("SRC=", 4, sb);
|
||
|
StyleBufferWrite(ptr, len, sb);
|
||
|
}
|
||
|
|
||
|
StyleBufferWrite(">');\n", 5, sb);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static char * open_style = "<style type=text/css>\\n";
|
||
|
static char * close_style = "</style>";
|
||
|
static char * disable_at_rules = "HI { donna : lou }\\n";
|
||
|
|
||
|
static void EchoCSS(char * src, int32 src_count, StyleBuffer sb)
|
||
|
{
|
||
|
char ch;
|
||
|
char * start;
|
||
|
char * end;
|
||
|
int len;
|
||
|
|
||
|
if (! src || src_count <= 0) return;
|
||
|
|
||
|
StyleBufferWrite(open_call, 0, sb);
|
||
|
StyleBufferWrite(open_style, 0, sb);
|
||
|
StyleBufferWrite(close_call, 0, sb);
|
||
|
|
||
|
/* Don't recurse on the @-rules!
|
||
|
* Write a harmless rule at the beginning of the style sheet.
|
||
|
* The lexer, parser, and finally the translator will see it.
|
||
|
* The translator will skip @ rules which occur after any
|
||
|
* ordinary rule. CSS requires that behavior for @import;
|
||
|
* @fontdef voluntarily observes the same restriction; there
|
||
|
* are no other recognized @ rules.
|
||
|
*/
|
||
|
StyleBufferWrite(open_call, 0, sb);
|
||
|
StyleBufferWrite(disable_at_rules, 0, sb);
|
||
|
StyleBufferWrite(close_call, 0, sb);
|
||
|
|
||
|
/* Echo the entire CSS buffer, escaping some characters. */
|
||
|
StyleBufferWrite(open_call, 0, sb);
|
||
|
start = src;
|
||
|
for (end = start; --src_count >=0; end++) {
|
||
|
|
||
|
ch = *end;
|
||
|
if (('\n' == ch) || ('\'' == ch) || ('\f' == ch) ||
|
||
|
('\v' == ch) || ('\r' == ch)) {
|
||
|
len = end - start;
|
||
|
if (len) {
|
||
|
StyleBufferWrite(start, len, sb);
|
||
|
start = end;
|
||
|
}
|
||
|
|
||
|
switch (ch) {
|
||
|
case '\n': StyleBufferWrite("\\n", 2, sb); break;
|
||
|
case '\'': StyleBufferWrite("\\'", 2, sb); break;
|
||
|
case '\f': StyleBufferWrite("\\f", 2, sb); break;
|
||
|
case '\v': StyleBufferWrite("\\v", 2, sb); break;
|
||
|
case '\r': StyleBufferWrite("\\r", 2, sb); break;
|
||
|
default : break;
|
||
|
}
|
||
|
|
||
|
start = end + 1;
|
||
|
}
|
||
|
}
|
||
|
len = end - start;
|
||
|
if (len) {
|
||
|
StyleBufferWrite(start, len, sb);
|
||
|
}
|
||
|
StyleBufferWrite(close_call, 0, sb);
|
||
|
|
||
|
StyleBufferWrite(open_call, 0, sb);
|
||
|
StyleBufferWrite(close_style, 0, sb);
|
||
|
StyleBufferWrite(close_call, 0, sb);
|
||
|
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifdef TEST_CSS_TRANSLATION
|
||
|
#define TEST_CSS_INITIAL_BUFSIZ 4096
|
||
|
#define TEST_CSS_INCR_BUFSIZ 4096
|
||
|
|
||
|
void main()
|
||
|
{
|
||
|
int ch;
|
||
|
char *b;
|
||
|
int32 src_count;
|
||
|
char *dst;
|
||
|
int32 dst_count;
|
||
|
char * src;
|
||
|
int src_size;
|
||
|
int done;
|
||
|
|
||
|
|
||
|
done = ch = 0;
|
||
|
src_size = src_count = 0;
|
||
|
|
||
|
src = (char *) XP_ALLOC(TEST_CSS_INITIAL_BUFSIZ);
|
||
|
if (src) src_size = TEST_CSS_INITIAL_BUFSIZ;
|
||
|
|
||
|
while (!done) {
|
||
|
|
||
|
for (b = src + src_count; src_count < src_size; src_count++, b++) {
|
||
|
*b = ch = getc(stdin);
|
||
|
/* Either the null character or EOF will serve to terminate. */
|
||
|
if (ch == EOF || ch == '\0') {
|
||
|
done = 1;
|
||
|
/* We don't need to add this character to the src_count */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!done) {
|
||
|
src_size += TEST_CSS_INCR_BUFSIZ;
|
||
|
src = (char *) XP_REALLOC(src, src_size);
|
||
|
if (!src) {
|
||
|
src_size = src_count = 0;
|
||
|
printf("css test: memory allocation failure\n");
|
||
|
done = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The src_count need not include the terminating NULL or EOF */
|
||
|
CSS_ConvertToJS(src, src_count, &dst, &dst_count);
|
||
|
printf("%s", dst);
|
||
|
XP_FREE(dst);
|
||
|
}
|
||
|
|
||
|
#endif
|