home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libstyle / csstojs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.9 KB  |  967 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* CSStoJS tranlation code */
  20.  
  21. #include "xp.h"
  22. #include "css.h"
  23. #include "cssI.h"
  24.  
  25. /* ALLOW_IMPORT
  26.  * Switch allows or stops the translation of @import rules.
  27.  */
  28. #define ALLOW_IMPORT 0
  29.  
  30. /* ALLOW_FONTDEF
  31.  * Switch allows or stops the translation of @fontdef rules.
  32.  */
  33. #define ALLOW_FONTDEF 1
  34.  
  35. /* ALLOW_PRIORITY
  36.  * Switch allows or stops the translation of "!important" in the value.
  37.  */
  38. #define ALLOW_PRIORITY 0
  39.  
  40. /* ALLOW_PSEUDO_ELEMENTS
  41.  * Switch allows or prevents the translation of rules with pseudo-elements
  42.  * :first-line and :first-letter in the selector.
  43.  */
  44. #define ALLOW_PSEUDO_ELEMENTS 0
  45.  
  46. /* ALLOW_ACTIVE_PSEUDO_CLASS
  47.  * Switch allows or prevents the translation of rules with pseudo-class
  48.  * :active in the selector.
  49.  */
  50. #define ALLOW_ACTIVE_PSEUDO_CLASS 0
  51.  
  52. #define STYLE_INITIAL_BUFSIZ 1024
  53. #define STYLE_INCR_BUFSIZ    1024
  54.  
  55. /* property type values */
  56. #define CSS_PROPERTY_TYPE_ASSIGNMENT (1)
  57. #define CSS_PROPERTY_TYPE_CALL       (2)
  58. /* pseudo class type values */
  59. #define CSS_PSEUDO_CLASS_NONE        (0)
  60. #define CSS_PSEUDO_CLASS_LINK        (1)
  61. #define CSS_PSEUDO_CLASS_ACTIVE        (2)
  62. #define CSS_PSEUDO_CLASS_VISITED    (3)
  63.  
  64. typedef struct {
  65.     char * buffer;
  66.     int32  buffer_size;   /* size of buffer in bytes */
  67.     int32  buffer_count;  /* bytes used */
  68.     int32  prior_count;   /* buffer_count at beginning of rule */
  69.     int32  pseudo_class_state; /* holds temporary state during translation */
  70.     XP_Bool ignore_rule;  /* during translation, may decide to ignore a rule */
  71. } *StyleBuffer, StyleBufferRec;
  72.  
  73. XP_BEGIN_PROTOS
  74.  
  75. static XP_Bool ConvertStyleSheet(css_node stylesheet, StyleBuffer sb);
  76. static void ConvertRule(css_node rule, StyleBuffer sb);
  77. static void ConvertSelector(css_node selector, css_node pseudo_element,
  78.                             StyleBuffer sb);
  79. static void ConvertContextualSelector(css_node selector, css_node pseudo_element,
  80.                                       StyleBuffer sb);
  81. static void ConvertSingleSelector(css_node simple, css_node pseudo_element,
  82.                                   StyleBuffer sb);
  83. static void ConvertDeclaration(css_node declaration_list, StyleBuffer sb);
  84. static void ConvertProperty(const char * css1_property, StyleBuffer sb,
  85.                             int * property_type_return);
  86. static void ConvertValue(css_node value, StyleBuffer sb, int property_type);
  87. static void ConvertURLValue(char * url_value, StyleBuffer sb);
  88. static void StripURLValue(char ** url_return, int * url_length_return);
  89. static void ConvertStringValue(char * string_value, StyleBuffer sb);
  90. static void StyleBufferWrite(char * str, int len, StyleBuffer sb);
  91. static void AnteRule(StyleBuffer sb);
  92. static void IgnoreRule(StyleBuffer sb);
  93. static void PostRule(StyleBuffer sb);
  94. static void SaltPseudoClass(const char * pseudo_class, StyleBuffer sb);
  95. static int32 GetPseudoClass(StyleBuffer sb);
  96.  
  97. #if (ALLOW_IMPORT || ALLOW_FONTDEF)
  98. static void ConvertToLink(css_node ruleset, int rel_type, StyleBuffer sb);
  99. static void EchoCSS(char * src, int32 src_count, StyleBuffer sb);
  100. #endif
  101.  
  102. XP_END_PROTOS
  103.  
  104. static char * input_buffer;
  105. static int32  input_buffer_count;   /* bytes used, not including NULL */
  106. static int32  input_buffer_index;
  107.  
  108. /* result and max_to_read are created and passed as int, not int32, on Win16. */
  109. void css_GetBuf(char * buf, int *result, int max_to_read)
  110. {
  111.     int i = 0;
  112.  
  113.     if (input_buffer) {
  114.  
  115.         while ((i < max_to_read) && (input_buffer_count > input_buffer_index))
  116.         buf[i++] = input_buffer[input_buffer_index++];
  117.  
  118.     if (0 == i) {
  119.         input_buffer = NULL;
  120.         input_buffer_count = input_buffer_index = 0;
  121.     }
  122.     }
  123.  
  124.     *result = i;
  125.     return;
  126. }
  127.  
  128. /* Java and JavaScript */
  129. static const char * reserved_words[] = {
  130.     "abstract",
  131.     "boolean",
  132.     "break",
  133.     "byte",
  134.     "case",
  135.     "catch",
  136.     "char",
  137.     "class",
  138.     "const",
  139.     "continue",
  140.     "default",
  141.     "delete",
  142.     "do",
  143.     "double",
  144.     "else",
  145.     "extends",
  146.     "false",
  147.     "final",
  148.     "finally",
  149.     "float",
  150.     "for",
  151.     "function",
  152.     "goto",
  153.     "if",
  154.     "implements",
  155.     "import",
  156.     "in",
  157.     "instanceof",
  158.     "int",
  159.     "interface",
  160.     "long",
  161.     "native",
  162.     "new",
  163.     "null",
  164.     "package",
  165.     "private",
  166.     "protected",
  167.     "public",
  168.     "return",
  169.     "short",
  170.     "static",
  171.     "super",
  172.     "switch",
  173.     "synchronized",
  174.     "this",
  175.     "throw",
  176.     "throws",
  177.     "transient",
  178.     "true",
  179.     "try",
  180.     "typeof",
  181.     "var",
  182.     "void",
  183.     "volatile",
  184.     "while",
  185.     "with"
  186. };
  187.  
  188. static XP_Bool IsReservedWord(const char * css_name)
  189. {
  190.     int lower = 0;
  191.     int upper = (sizeof(reserved_words) / sizeof(char *)) - 1;
  192.     int x;
  193.     int32 result;
  194.  
  195.     while (upper >= lower) {
  196.         x = (lower + upper) / 2;
  197.         result = XP_STRCMP(css_name, reserved_words[x]);
  198.         if (result < 0)
  199.             upper = x - 1;
  200.         else if (result > 0)
  201.             lower = x + 1;
  202.         else
  203.             return 1;
  204.     }
  205.     return 0;
  206. }
  207.  
  208. /* CSS_ConvertToJSCompatibleName
  209.  * converts characters which are illegal in JavaScript names.
  210.  *
  211.  *  Anne-Marie ==> Anne_Marie     "-" (dash) always converts to "_"
  212.  *  5NewportRd ==> _5NewportRd    1st character a digit prepend "_"
  213.  *  Convert lower to upper case when second parameter is true.
  214.  *  When second parameter is false, prepend "_" to reserved words.
  215.  * Returns NULL on error.
  216.  */
  217. PUBLIC char *
  218. CSS_ConvertToJSCompatibleName(char *css_name, XP_Bool uppercase_it)
  219. {
  220.     int32 len;
  221.     char *new_name;
  222.     char *cp;
  223.         XP_Bool reserved = 0;
  224.  
  225.     XP_ASSERT(css_name);
  226.  
  227.     if (!css_name)
  228.         return NULL;
  229.  
  230.     len = XP_STRLEN(css_name);
  231.         if (! uppercase_it)
  232.             reserved = IsReservedWord(css_name);
  233.     if (reserved || XP_IS_DIGIT(*css_name))
  234.         len++;
  235.  
  236.     cp = new_name = XP_ALLOC((len+1) * sizeof(char));
  237.     if (!new_name)
  238.         return NULL;
  239.  
  240.     if (reserved || XP_IS_DIGIT(*css_name)) {
  241.         *cp = '_';
  242.         cp++;
  243.     }
  244.  
  245.     for(; *css_name; css_name++, cp++)
  246.         if (*css_name == '-')
  247.             *cp = '_';
  248.         else 
  249.             *cp = uppercase_it ? (XP_TO_UPPER(*css_name)) : *css_name;
  250.  
  251.     *cp = '\0';
  252.  
  253.     return new_name;
  254. }
  255.  
  256.  
  257. PUBLIC
  258. void CSS_ConvertToJS(char * src, int32 src_count, char ** dst, int32 * dst_count)
  259. {
  260.     StyleBuffer sb;
  261.     XP_Bool translated;
  262.  
  263.     *dst = NULL;
  264.     *dst_count = 0;
  265.  
  266.     if (NULL == src || 0 == src_count)
  267.         return;
  268.  
  269.     input_buffer = src;
  270.     input_buffer_count = src_count;
  271.     input_buffer_index = 0;
  272.  
  273.     css_tree_root = NULL;
  274.     if (css_parse() != 0)
  275.         return;
  276.  
  277.     sb = XP_NEW_ZAP(StyleBufferRec);
  278.     if (sb == NULL)
  279.         return;
  280.  
  281.     translated = ConvertStyleSheet(css_tree_root, sb);
  282.     css_FreeNode(css_tree_root);
  283. #if ALLOW_IMPORT
  284.     if (! translated)
  285.         EchoCSS(src, src_count, sb);
  286. #endif
  287.  
  288.     /* The caller should free *dst. */
  289.     *dst = sb->buffer;
  290.     *dst_count = sb->buffer_count;
  291.     XP_DELETE(sb);
  292. }
  293.  
  294.  
  295. /* Release all subtree and the node */
  296. void css_FreeNode(css_node vp)
  297.     if (vp) {
  298.         css_FreeNode(vp->left);
  299.         css_FreeNode(vp->right);
  300.         if (vp->string)
  301.             XP_FREE(vp->string);
  302.         XP_DELETE(vp);
  303.     }
  304. }
  305.  
  306.  
  307. static XP_Bool ConvertStyleSheet(css_node stylesheet, StyleBuffer sb)
  308. {
  309.  
  310. #if (ALLOW_IMPORT || ALLOW_FONTDEF)
  311.     XP_Bool translate_rules = 1;
  312.  
  313.     /* CSS1 requires @import productions occurring after
  314.      * ruleset productions to be ignored.  This is implemented
  315.      * here.  The restriction is not enforced by the parser.
  316.      */
  317.     for (; stylesheet && (stylesheet->node_id != NODE_RULESET_LIST);
  318.          stylesheet = stylesheet->left) {
  319. #if ALLOW_IMPORT
  320.         if (NODE_IMPORT_LIST == stylesheet->node_id) {
  321.             ConvertToLink(stylesheet->right, stylesheet->node_id, sb);
  322.             translate_rules = 0;
  323.         }
  324. #endif
  325. #if ALLOW_FONTDEF
  326.         if (NODE_FONTDEF_LIST == stylesheet->node_id) {
  327.             ConvertToLink(stylesheet->right, stylesheet->node_id, sb);
  328.         }
  329. #endif
  330.     }
  331. #if (ALLOW_IMPORT)
  332.     if (! translate_rules)
  333.         return 0;
  334. #endif
  335. #endif
  336.  
  337.     for (; stylesheet; stylesheet = stylesheet->left) {
  338.         if (stylesheet->node_id == NODE_RULESET_LIST)
  339.             ConvertRule(stylesheet->right, sb);
  340.     }
  341.  
  342.     return 1;
  343. }
  344.  
  345.  
  346. /* Generate the JavaScript corresponding to one CSS1 rule. */
  347. static void ConvertRule(css_node rule, StyleBuffer sb)
  348. {
  349.     css_node selector_list, selector;
  350.     css_node declaration_list;
  351.  
  352.     if (! rule || rule->node_id != NODE_RULESET)
  353.         return;
  354.  
  355.     for (selector_list = rule->left; selector_list; 
  356.          selector_list = selector_list->left) {
  357.  
  358.         selector = selector_list->right;
  359.         if (! selector)
  360.             continue;
  361.  
  362.         for (declaration_list = rule->right; declaration_list;
  363.              declaration_list = declaration_list->left) {
  364.  
  365.             if (declaration_list->right) {
  366.                 AnteRule(sb);
  367.                 ConvertSelector(selector->left, selector->right, sb);
  368.                 ConvertDeclaration(declaration_list->right, sb);
  369.                 PostRule(sb);
  370.             }
  371.         }
  372.     }
  373. }
  374.  
  375. static void AnteRule(StyleBuffer sb)
  376. {
  377.     sb->prior_count = sb->buffer_count;
  378.     sb->pseudo_class_state = CSS_PSEUDO_CLASS_NONE;
  379. }
  380.  
  381. static void PostRule(StyleBuffer sb)
  382. {
  383.     if (sb->ignore_rule) {
  384.         sb->buffer_count = sb->prior_count;
  385.         sb->ignore_rule = 0;
  386.         if (0 == sb->buffer_count)
  387.             sb->buffer_count = 1;
  388.         sb->buffer[sb->buffer_count-1] = '\0';
  389.     }
  390. }
  391.  
  392. static void StyleBufferWrite(char * str, int len, StyleBuffer sb)
  393. {
  394.  
  395.     if (str == NULL)
  396.         return;
  397.  
  398.     if (len <= 0) {
  399.         len = strlen(str);
  400.         if (len <= 0)
  401.             return;
  402.     }
  403.  
  404.     if (sb->buffer == NULL) {
  405.         sb->buffer = (char *) XP_ALLOC(STYLE_INITIAL_BUFSIZ);
  406.         if (sb->buffer) {
  407.             sb->buffer_size = STYLE_INITIAL_BUFSIZ;
  408.             *sb->buffer = '\0';
  409.             sb->buffer_count = 1;
  410.         }
  411.     }
  412.  
  413.     if (sb->buffer_count + len > sb->buffer_size) {
  414.         sb->buffer = (char *)
  415.             XP_REALLOC(sb->buffer, sb->buffer_size + STYLE_INCR_BUFSIZ);
  416.         if (sb->buffer) {
  417.             sb->buffer_size += STYLE_INCR_BUFSIZ;
  418.         } else {
  419.             sb->buffer_size = sb->buffer_count = 0;
  420.         }
  421.     }
  422.  
  423.     if (sb->buffer_count + len <= sb->buffer_size) {
  424.         (void) strncpy(sb->buffer + sb->buffer_count - 1, str, len);
  425.         sb->buffer_count += len;
  426.         sb->buffer[sb->buffer_count-1] = '\0';
  427.     }
  428. }
  429.  
  430.  
  431. static void ConvertSelector(css_node selector, css_node pseudo_element,
  432.                             StyleBuffer sb)
  433. {
  434.     if (selector->node_id == NODE_SELECTOR_CONTEXTUAL) {
  435.         StyleBufferWrite("contextual(", 11, sb);
  436.         ConvertContextualSelector(selector, pseudo_element, sb);
  437.         StyleBufferWrite(")", 1, sb);
  438.     } else {
  439.         ConvertSingleSelector(selector, pseudo_element, sb);
  440.     }
  441. }
  442.  
  443.  
  444. static void ConvertContextualSelector(css_node selector,
  445.                                       css_node pseudo_element, StyleBuffer sb)
  446. {
  447.     if (selector->left) {
  448.         /* pseudo_elements may be present only on the final context object */
  449.         ConvertContextualSelector(selector->left, NULL, sb);
  450.         StyleBufferWrite(", ", 2, sb);
  451.     }
  452.  
  453.     ConvertSingleSelector(selector->right, pseudo_element, sb);
  454. }
  455.  
  456.  
  457. static void ConvertSingleSelector(css_node selector, css_node pseudo_element,
  458.                                   StyleBuffer sb)
  459. {
  460.     char * str1;
  461.     char * str2;
  462.  
  463.     if (! selector)
  464.         return;
  465.  
  466.     if (selector->node_id == NODE_WILD) {
  467.         /* CSS1 Section 7.1 states: "A ruleset that [has] a selector
  468.          * string that is not valid CSS1 is skipped."  (sic)
  469.          */
  470.         IgnoreRule(sb);
  471.     }
  472.  
  473.     if (! selector->left)
  474.         return;
  475.  
  476.     str1 = selector->left->string;
  477.     str2 = selector->right ? selector->right->string : NULL;
  478.  
  479.     switch (selector->node_id) {
  480.  
  481.     case NODE_SIMPLE_SELECTOR_NAME_ONLY:
  482.  
  483.         /* "document.tags.<TAG>" */
  484.         StyleBufferWrite("document.tags.", 14, sb);
  485.         str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
  486.         StyleBufferWrite(str1, 0, sb);
  487.         XP_FREE(str1);
  488.         break;
  489.  
  490.     case NODE_SIMPLE_SELECTOR_DOT_AND_CLASS:
  491.  
  492.         /* "document.classes.<class>.all" */
  493.         StyleBufferWrite("document.classes.", 17, sb);
  494.         str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
  495.         StyleBufferWrite(str1, 0, sb);
  496.         XP_FREE(str1);
  497.         StyleBufferWrite(".all", 4, sb);
  498.         break;
  499.  
  500.     case NODE_SIMPLE_SELECTOR_ID_SELECTOR:
  501.  
  502.         /* "document.ids.<id>" */
  503.         StyleBufferWrite("document.ids.", 13, sb);
  504.         str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
  505.         StyleBufferWrite(str1, 0, sb);
  506.         XP_FREE(str1);
  507.         break;
  508.  
  509.     case NODE_SIMPLE_SELECTOR_NAME_AND_CLASS:
  510.  
  511.         /* "document.classes.<class>.<TAG>" */
  512.         StyleBufferWrite("document.classes.", 17, sb);
  513.         str2 = CSS_ConvertToJSCompatibleName(str2, FALSE);
  514.         StyleBufferWrite(str2, 0, sb);
  515.         XP_FREE(str2);
  516.         StyleBufferWrite(".", 1, sb);
  517.         str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
  518.         StyleBufferWrite(str1, 0, sb);
  519.         XP_FREE(str1);
  520.         break;
  521.  
  522.     case NODE_SIMPLE_SELECTOR_NAME_PSEUDO_CLASS:
  523.  
  524.         /* "document.tags.<TAG> */
  525.         /* By design, prepend the pseudoclass name to the property
  526.          * name when the property is 'color'; otherwise, ignore the
  527.          * pseudoclass name.
  528.          */
  529.         StyleBufferWrite("document.tags.", 14, sb);
  530.         str1 = CSS_ConvertToJSCompatibleName(str1, TRUE);
  531.         StyleBufferWrite(str1, 0, sb);
  532.         XP_FREE(str1);
  533.         SaltPseudoClass(str2, sb);
  534.         break;
  535.  
  536.     case NODE_SIMPLE_SELECTOR_NAME_CLASS_PSEUDO_CLASS:
  537.  
  538.         /* "document.classes.<class>.<TAG>" */
  539.         /* By design, prepend the pseudoclass name to the property
  540.          * name when the property is 'color'; otherwise, ignore the
  541.          * pseudoclass name.
  542.          */
  543.         StyleBufferWrite("document.classes.", 17, sb);
  544.         str1 = CSS_ConvertToJSCompatibleName(str1, FALSE);
  545.         StyleBufferWrite(str1, 0, sb);
  546.         XP_FREE(str1);
  547.         StyleBufferWrite(".", 1, sb);
  548.         str1 = CSS_ConvertToJSCompatibleName(selector->string, TRUE);
  549.         StyleBufferWrite(str1, 0, sb);
  550.         XP_FREE(str1);
  551.         SaltPseudoClass(str2, sb);
  552.         break;
  553.  
  554.     default:
  555.         break;
  556.     }
  557.  
  558.     if (pseudo_element) {
  559. #if ! ALLOW_PSEUDO_ELEMENTS
  560.         IgnoreRule(sb);
  561. #endif
  562.         StyleBufferWrite(".", 1, sb);
  563.         ConvertProperty(pseudo_element->string, sb, NULL);
  564.     }
  565. }
  566.  
  567. static void IgnoreRule(StyleBuffer sb)
  568. {
  569.     sb->ignore_rule = 1;
  570. }
  571.  
  572. static void SaltPseudoClass(const char * pseudo_class, StyleBuffer sb)
  573. {
  574.     if (! pseudo_class)
  575.         return;
  576.  
  577.     /* The parser always gives these strings in lower case. */
  578.     if (0 == XP_STRCMP(pseudo_class, "link"))
  579.         sb->pseudo_class_state = CSS_PSEUDO_CLASS_LINK;
  580.     else if (0 == XP_STRCMP(pseudo_class, "visited"))
  581.         sb->pseudo_class_state = CSS_PSEUDO_CLASS_VISITED;
  582.     else if (0 == XP_STRCMP(pseudo_class, "active")) {
  583. #if (! ALLOW_ACTIVE_PSEUDO_CLASS)
  584.         IgnoreRule(sb);
  585. #endif
  586.         sb->pseudo_class_state = CSS_PSEUDO_CLASS_ACTIVE;
  587.     }
  588. }
  589.  
  590. static int32 GetPseudoClass(StyleBuffer sb)
  591. {
  592.     return sb->pseudo_class_state;
  593. }
  594.  
  595. /* Pseudo-elements as well as property names are converted by this. */
  596. static void ConvertProperty(const char * css1_property, StyleBuffer sb, 
  597.                             int * property_type_return)
  598. {
  599.     XP_Bool upper;
  600.     char ch[2];
  601.     int32 pseudo_class;
  602.  
  603.     ch[1] = '\0';
  604.  
  605.     if (property_type_return)
  606.         *property_type_return = CSS_PROPERTY_TYPE_ASSIGNMENT;
  607.  
  608.     /* The CSS1 float property is translated to align because float is
  609.      * a reserved word in JavaScript.
  610.      */
  611.     if (0 == XP_STRCASECMP(css1_property, "float"))
  612.         StyleBufferWrite("align", 5, sb);
  613.     else {
  614.         if (property_type_return) {
  615.             if (   (0 == XP_STRCASECMP(css1_property, "margin"))
  616.                 || (0 == XP_STRCASECMP(css1_property, "padding"))
  617.                 || (0 == XP_STRCASECMP(css1_property, "border-width")))
  618.                 *property_type_return = CSS_PROPERTY_TYPE_CALL;
  619.         }
  620.  
  621.         pseudo_class = GetPseudoClass(sb);
  622.  
  623.         if (0 == XP_STRCASECMP(css1_property, "margin"))
  624.             StyleBufferWrite("margins", 7, sb);
  625.         else if (0 == XP_STRCASECMP(css1_property, "padding"))
  626.             StyleBufferWrite("paddings", 8, sb);
  627.         else if (0 == XP_STRCASECMP(css1_property, "border-width"))
  628.             StyleBufferWrite("borderWidths", 12, sb);
  629.         else if ((CSS_PSEUDO_CLASS_NONE != pseudo_class)
  630.                  && (0 == XP_STRCASECMP(css1_property, "color"))) {
  631.             switch (pseudo_class) {
  632.             case CSS_PSEUDO_CLASS_LINK:
  633.             default:
  634.                 StyleBufferWrite("linkColor", 9, sb);
  635.                 break;
  636.             case CSS_PSEUDO_CLASS_ACTIVE:
  637.                 StyleBufferWrite("activeColor", 11, sb);
  638.                 break;
  639.             case CSS_PSEUDO_CLASS_VISITED:
  640.                 StyleBufferWrite("visitedColor", 12, sb);
  641.                 break;
  642.             }
  643.         }
  644.         else {
  645.             for (upper=0; *css1_property; css1_property++) {
  646.  
  647.                 if (*css1_property == '-') {
  648.                     upper = 1;
  649.                     continue;
  650.                 }
  651.        
  652.                 ch[0] = (upper ? toupper(*css1_property)
  653.                          : tolower(*css1_property));
  654.                 upper = 0;
  655.                 StyleBufferWrite(ch, 1, sb);
  656.             }
  657.         }
  658.     }
  659. }
  660.  
  661.  
  662. static void ConvertDeclaration(css_node declaration, StyleBuffer sb)
  663. {
  664.     int property_type;
  665.  
  666.     if (declaration && 
  667.         (declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR ||
  668.          declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR_PRIO)) {
  669.         StyleBufferWrite(".", 1, sb);
  670.         ConvertProperty(declaration->left->string, sb, &property_type);
  671.         ConvertValue(declaration->right, sb, property_type);
  672. #if ALLOW_PRIORITY
  673.         if (declaration->node_id == NODE_DECLARATION_PROPERTY_EXPR_PRIO)
  674.             StyleBufferWrite(" !important", 11, sb);
  675. #endif
  676.         StyleBufferWrite("\n", 1, sb);
  677.     }
  678. }
  679.  
  680.  
  681. static void ConvertValue(css_node value, StyleBuffer sb, int property_type)
  682. {
  683.     css_node term;
  684.     XP_Bool comma;
  685.  
  686.  
  687.     if (CSS_PROPERTY_TYPE_ASSIGNMENT == property_type)
  688.         StyleBufferWrite(" = \"", 4, sb);
  689.     else
  690.         StyleBufferWrite("(", 1, sb);
  691.  
  692.     for (comma=FALSE; value; value=value->left, comma = TRUE) {
  693.  
  694.         if (CSS_PROPERTY_TYPE_CALL == property_type && comma)
  695.             StyleBufferWrite(",", 1, sb);
  696.  
  697.         if (value->node_id == NODE_EMPTY_OP)
  698.             StyleBufferWrite(" ", 1, sb);
  699.  
  700.         if (CSS_PROPERTY_TYPE_CALL == property_type)
  701.             StyleBufferWrite("\"", 1, sb);
  702.  
  703.         if (value->node_id == NODE_EXPR_OP)
  704.             StyleBufferWrite(value->string, 0, sb);
  705.  
  706.         term = value->right;
  707.         if (term) {
  708.             if (term->node_id == NODE_STRING)
  709.                 ConvertStringValue(term->string, sb);
  710.             else if (NODE_URL == term->node_id)
  711.                 ConvertURLValue(term->string, sb);
  712.             else {
  713.                 StyleBufferWrite(term->string, 0, sb);
  714.                 if (term->node_id == NODE_UNARY_OP)
  715.                     StyleBufferWrite(term->left->string, 0, sb);
  716.             }
  717.         }
  718.  
  719.         if (CSS_PROPERTY_TYPE_CALL == property_type)
  720.             StyleBufferWrite("\"", 1, sb);
  721.     }
  722.  
  723.     if (CSS_PROPERTY_TYPE_ASSIGNMENT == property_type)
  724.         StyleBufferWrite("\"", 1, sb);
  725.     else
  726.         StyleBufferWrite(")", 1, sb);
  727. }
  728.  
  729.  
  730. static void ConvertStringValue(char * string_value, StyleBuffer sb)
  731. {
  732.     for (; *string_value; string_value++) {
  733.         if (*string_value == '"')
  734.             StyleBufferWrite("\\", 1, sb);
  735.         StyleBufferWrite(string_value, 1, sb);
  736.     }
  737. }
  738.  
  739. static void StripURLValue(char ** url_return, int * url_length_return)
  740. {
  741.     char * ptr;
  742.     int len;
  743.  
  744.     if (! url_return || ! *url_return ||
  745.         ! url_length_return || ! *url_length_return)
  746.         return;
  747.  
  748.     ptr = *url_return;
  749.     len = *url_length_return;
  750.  
  751.     /* Skip leading whitespace */
  752.     while (len > 0 && isspace(*ptr)) {
  753.         ptr++;
  754.         len--;
  755.     }
  756.  
  757.     /* Skip trailing whitespace */
  758.     while (len > 0 && isspace(*(ptr + len - 1)))
  759.         len--;
  760.  
  761.     /* Skip enclosing quotation marks */
  762.     if (('"' == *ptr || '\'' == *ptr ) &&
  763.         (len > 1) &&
  764.         (*ptr == *(ptr + len - 1))) {
  765.         ptr++;
  766.         len -= 2;
  767.     }
  768.  
  769.     *url_return = ptr;
  770.     *url_length_return = len;
  771. }
  772.  
  773. static void ConvertURLValue(char * url_value, StyleBuffer sb)
  774. {
  775.     char * ptr = url_value;
  776.     int len;
  777.  
  778.     len = strlen(ptr);
  779.     if (len < 6)
  780.         return;
  781.  
  782.     /* Don't count the 5 characters for 'url()' */
  783.     len -= 5;
  784.     StyleBufferWrite("url(", 4, sb);
  785.     ptr += 4;
  786.  
  787.     StripURLValue(&ptr, &len);
  788.  
  789.     if (len > 0)
  790.         StyleBufferWrite(ptr, len, sb);
  791.  
  792.     StyleBufferWrite(")", 1, sb);
  793. }
  794.  
  795. #if (ALLOW_IMPORT || ALLOW_FONTDEF)
  796.  
  797. static char * open_call   = "document.write('";
  798. static char * close_call   = "');\n";
  799.  
  800. static void ConvertToLink(css_node rule, int rel_type, StyleBuffer sb)
  801. {
  802.     int len;
  803.     char * ptr;
  804.     char ch;
  805.     char hexbuf[4];
  806.  
  807.     if (rule && rule->string) {
  808.         if ((NODE_IMPORT_URL == rule->node_id) || 
  809.             (NODE_IMPORT_STRING == rule->node_id)) {
  810.  
  811.             StyleBufferWrite(open_call, 0, sb);
  812.  
  813.             if (NODE_IMPORT_LIST == rel_type)
  814.                 StyleBufferWrite("<LINK REL=stylesheet ", 21, sb);
  815.             else
  816.                 StyleBufferWrite("<LINK REL=fontdef ", 18, sb);
  817.  
  818.             /* Copy the url */
  819.             len = XP_STRLEN(rule->string);
  820.             ptr = NULL;
  821.             if ((NODE_IMPORT_URL == rule->node_id) && (len > 5)) {
  822.                 /* Copy the characters inside the parens */
  823.                 ptr = rule->string + 4;
  824.                 len -= 5;
  825.                 StripURLValue(&ptr, &len);
  826.                 if (len <= 0)
  827.                     ptr = NULL;
  828.             } else {
  829.                 /* Copy the characters inside the quotes */
  830.                 ptr = rule->string + 1;
  831.                 len -= 2;
  832.             }
  833.             if (NULL != ptr) {
  834.                 if (NODE_IMPORT_LIST == rel_type)
  835.                     StyleBufferWrite("HREF=", 5, sb);
  836.                 else
  837.                     StyleBufferWrite("SRC=", 4, sb);
  838.                 StyleBufferWrite(ptr, len, sb);
  839.             }
  840.  
  841.             StyleBufferWrite(">');\n", 5, sb);
  842.         }
  843.     }
  844. }
  845.  
  846. static char * open_style = "<style type=text/css>\\n";
  847. static char * close_style = "</style>";
  848. static char * disable_at_rules = "HI { donna : lou }\\n";
  849.  
  850. static void EchoCSS(char * src, int32 src_count, StyleBuffer sb)
  851. {
  852.     char ch;
  853.     char * start;
  854.     char * end;
  855.     int len;
  856.  
  857.     if (! src || src_count <= 0) return;
  858.  
  859.     StyleBufferWrite(open_call, 0, sb);
  860.     StyleBufferWrite(open_style, 0, sb);
  861.     StyleBufferWrite(close_call, 0, sb);
  862.  
  863.     /* Don't recurse on the @-rules! 
  864.      * Write a harmless rule at the beginning of the style sheet.
  865.      * The lexer, parser, and finally the translator will see it.
  866.      * The translator will skip @ rules which occur after any
  867.      * ordinary rule.  CSS requires that behavior for @import;
  868.      * @fontdef voluntarily observes the same restriction; there
  869.      * are no other recognized @ rules.
  870.      */
  871.     StyleBufferWrite(open_call, 0, sb);
  872.     StyleBufferWrite(disable_at_rules, 0, sb);
  873.     StyleBufferWrite(close_call, 0, sb);
  874.  
  875.     /* Echo the entire CSS buffer, escaping some characters. */
  876.     StyleBufferWrite(open_call, 0, sb);
  877.     start = src;
  878.     for (end = start; --src_count >=0; end++) {
  879.  
  880.         ch = *end;
  881.         if (('\n' == ch) || ('\'' == ch) || ('\f' == ch) ||
  882.             ('\v' == ch) || ('\r' == ch)) {
  883.             len = end - start;
  884.             if (len) {
  885.                 StyleBufferWrite(start, len, sb);
  886.                 start = end;
  887.             }
  888.  
  889.             switch (ch) {
  890.             case '\n': StyleBufferWrite("\\n", 2, sb);    break;
  891.             case '\'': StyleBufferWrite("\\'", 2, sb);    break;
  892.             case '\f': StyleBufferWrite("\\f", 2, sb);    break;
  893.             case '\v': StyleBufferWrite("\\v", 2, sb);    break;
  894.             case '\r': StyleBufferWrite("\\r", 2, sb);    break;
  895.             default  : break;
  896.             }
  897.             
  898.             start = end + 1;
  899.         }
  900.     }
  901.     len = end - start;
  902.     if (len) {
  903.         StyleBufferWrite(start, len, sb);
  904.     }
  905.     StyleBufferWrite(close_call, 0, sb);
  906.  
  907.     StyleBufferWrite(open_call, 0, sb);
  908.     StyleBufferWrite(close_style, 0, sb);
  909.     StyleBufferWrite(close_call, 0, sb);
  910.  
  911. }
  912. #endif
  913.  
  914. #ifdef TEST_CSS_TRANSLATION
  915. #define TEST_CSS_INITIAL_BUFSIZ 4096
  916. #define TEST_CSS_INCR_BUFSIZ    4096
  917.  
  918. void main()
  919. {
  920.     int ch;
  921.     char *b;
  922.     int32 src_count;
  923.     char *dst;
  924.     int32 dst_count;
  925.     char * src;
  926.     int src_size;
  927.     int done;
  928.  
  929.         
  930.     done = ch = 0;
  931.     src_size = src_count = 0;
  932.  
  933.     src = (char *) XP_ALLOC(TEST_CSS_INITIAL_BUFSIZ);
  934.     if (src) src_size = TEST_CSS_INITIAL_BUFSIZ;
  935.  
  936.     while (!done) {
  937.  
  938.         for (b = src + src_count; src_count < src_size; src_count++, b++) {
  939.             *b = ch = getc(stdin);
  940.             /* Either the null character or EOF will serve to terminate. */
  941.             if (ch == EOF || ch == '\0') {
  942.                 done = 1;
  943.                 /* We don't need to add this character to the src_count */
  944.                 break;
  945.             }
  946.         }
  947.  
  948.         if (!done) {
  949.             src_size += TEST_CSS_INCR_BUFSIZ;
  950.             src = (char *) XP_REALLOC(src, src_size);
  951.             if (!src) {
  952.                 src_size = src_count = 0;
  953.                 printf("css test: memory allocation failure\n");
  954.                 done = 1;
  955.             }
  956.         }
  957.     }
  958.  
  959.     /* The src_count need not include the terminating NULL or EOF */
  960.     CSS_ConvertToJS(src, src_count, &dst, &dst_count);
  961.     printf("%s", dst);
  962.     XP_FREE(dst);
  963. }
  964.  
  965. #endif
  966.