home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libstyle / libstyle.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  30.9 KB  |  1,168 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. #include "xp.h"
  20. #include "jsapi.h"
  21. #include "libstyle.h"
  22. #include "xp_mcom.h"
  23. #include "jsspriv.h"
  24. #include "jssrules.h"
  25. #include "jsscope.h"
  26. #include "jsatom.h"
  27.  
  28. #ifndef NSPR20
  29. #include "prhash.h"
  30. #else
  31. #include "plhash.h"
  32. #endif
  33.  
  34. extern void LO_SetStyleObjectRefs(MWContext *context, void *tags, void *classes, void *ids);
  35.  
  36. /**** Declaration of JavaScript classes ****/
  37.  
  38. extern JSClass Tags_class;
  39. extern JSClass Classes_class;
  40. extern JSClass Tag_class;
  41.  
  42. static JSBool
  43. is_valid_jsss_prop(char *prop)
  44. {
  45.     if(!prop)
  46.         return FALSE;
  47.  
  48.     switch(XP_TO_UPPER(*prop))
  49.     {
  50.         case 'A':
  51.             if(!strcasecomp(prop, "absolute"))
  52.                 return TRUE;
  53.             else if(!strcasecomp(prop, "activeColor"))
  54.                 return TRUE;
  55.             else if(!strcasecomp(prop, "align"))
  56.                 return TRUE;
  57.             break;
  58.         case 'B':
  59.             if(!strcasecomp(prop, "borderWidth"))
  60.                 return TRUE;
  61.             else if(!strcasecomp(prop, "borderStyle"))
  62.                 return TRUE;
  63.             else if(!strcasecomp(prop, "borderColor"))
  64.                 return TRUE;
  65.             else if(!strcasecomp(prop, "borderRightWidth"))
  66.                 return TRUE;
  67.             else if(!strcasecomp(prop, "borderLeftWidth"))
  68.                 return TRUE;
  69.             else if(!strcasecomp(prop, "borderTopWidth"))
  70.                 return TRUE;
  71.             else if(!strcasecomp(prop, "borderBottomWidth"))
  72.                 return TRUE;
  73.             else if(!strcasecomp(prop, "backgroundColor"))
  74.                 return TRUE;
  75.             else if(!strcasecomp(prop, "backgroundImage"))
  76.                 return TRUE;
  77.             else if(!strcasecomp(prop, "backgroundRepeat"))
  78.                 return TRUE;
  79.             break;
  80.         case 'C':
  81.             if(!strcasecomp(prop, "clear"))
  82.                 return TRUE;
  83.             else if(!strcasecomp(prop, "color"))
  84.                 return TRUE;
  85.             else if(!strcasecomp(prop, "clip"))
  86.                 return TRUE;
  87.             break;
  88.         case 'D':
  89.             if(!strcasecomp(prop, "display"))
  90.                 return TRUE;
  91.             break;
  92.         case 'F':
  93.             if(!strcasecomp(prop, "fontSize"))
  94.                 return TRUE;
  95.             else if(!strcasecomp(prop, "fontFamily"))
  96.                 return TRUE;
  97.             else if(!strcasecomp(prop, "fontWeight"))
  98.                 return TRUE;
  99.             else if(!strcasecomp(prop, "fontStyle"))
  100.                 return TRUE;
  101.             break;
  102.         case 'H':
  103.             if(!strcasecomp(prop, "height"))
  104.                 return TRUE;
  105.             break;
  106.         case 'I':
  107.             if(!strcasecomp(prop, "includeSource"))
  108.                 return TRUE;
  109.             break;
  110.         case 'L':
  111.             if(!strcasecomp(prop, "lineHeight"))
  112.                 return TRUE;
  113.             else if(!strcasecomp(prop, "listStyleType"))
  114.                 return TRUE;
  115.             else if(!strcasecomp(prop, "layerBackgroundColor"))
  116.                 return TRUE;
  117.             else if(!strcasecomp(prop, "layerBackgroundImage"))
  118.                 return TRUE;
  119.             else if(!strcasecomp(prop, "linkColor"))
  120.                 return TRUE;
  121.             else if(!strcasecomp(prop, "linkBorder"))
  122.                 return TRUE;
  123.             else if(!strcasecomp(prop, "_layer_width"))
  124.                 return TRUE;
  125.             else if(!strcasecomp(prop, "left"))
  126.                 return TRUE;
  127.             break;
  128.         case 'M':
  129.             if(!strcasecomp(prop, "marginLeft"))
  130.                 return TRUE;
  131.             else if(!strcasecomp(prop, "marginRight"))
  132.                 return TRUE;
  133.             else if(!strcasecomp(prop, "marginTop"))
  134.                 return TRUE;
  135.             else if(!strcasecomp(prop, "marginBottom"))
  136.                 return TRUE;
  137.             break;
  138.         case 'O':
  139.             if(!strcasecomp(prop, "overflow"))
  140.                 return TRUE;
  141.             break;
  142.         case 'P':
  143.             if(!strcasecomp(prop, "padding"))
  144.                 return TRUE;
  145.             else if(!strcasecomp(prop, "paddingLeft"))
  146.                 return TRUE;
  147.             else if(!strcasecomp(prop, "paddingRight"))
  148.                 return TRUE;
  149.             else if(!strcasecomp(prop, "paddingTop"))
  150.                 return TRUE;
  151.             else if(!strcasecomp(prop, "paddingBottom"))
  152.                 return TRUE;
  153.             else if(!strcasecomp(prop, "position"))
  154.                 return TRUE;
  155.             else if(!strcasecomp(prop, "pageBreakBefore"))
  156.                 return TRUE;
  157.             else if(!strcasecomp(prop, "pageBreakAfter"))
  158.                 return TRUE;
  159.             break;
  160.         case 'R':
  161.             if(!strcasecomp(prop, "relative"))
  162.                 return TRUE;
  163.             break;
  164.         case 'T':
  165.             if(!strcasecomp(prop, "top"))
  166.                 return TRUE;
  167.             else if(!strcasecomp(prop, "textTransform"))
  168.                 return TRUE;
  169.             else if(!strcasecomp(prop, "textAlign"))
  170.                 return TRUE;
  171.             else if(!strcasecomp(prop, "textIndent"))
  172.                 return TRUE;
  173.             else if(!strcasecomp(prop, "textDecoration"))
  174.                 return TRUE;
  175.             break;
  176.         case 'V':
  177.             if(!strcasecomp(prop, "visitedColor"))
  178.                 return TRUE;
  179.             else if(!strcasecomp(prop, "verticalAlign"))
  180.                 return TRUE;
  181.             else if(!strcasecomp(prop, "visibility"))
  182.                 return TRUE;
  183.             break;
  184.         case 'W':
  185.             if(!strcasecomp(prop, "width"))
  186.                 return TRUE;
  187.             if(!strcasecomp(prop, "whiteSpace"))
  188.                 return TRUE;
  189.             break;
  190.         case 'Z':
  191.             if(!strcasecomp(prop, "zIndex"))
  192.                 return TRUE;
  193.         default:
  194.             XP_ASSERT(0);
  195.             break;
  196.     }
  197.  
  198.     return FALSE;
  199. };
  200.  
  201. int PR_CALLBACK
  202. jss_CompareStringsNoCase(const void *str1, const void *str2)
  203. {
  204.     return XP_STRCASECMP(str1, str2) == 0;
  205. }
  206.  
  207. PRHashNumber PR_CALLBACK
  208. jss_HashStringNoCase(const void *key)
  209. {
  210.     PRHashNumber h;
  211.     const unsigned char *s;
  212.  
  213.     h = 0;
  214.     for (s = key; *s; s++)
  215.         h = (h >> 28) ^ (h << 4) ^ toupper(*s);
  216.     return h;
  217. }
  218.  
  219. /**** Finalizer for JSSTags and JSSClasses ****/
  220.  
  221. int PR_CALLBACK
  222. jss_DestroyTags(PRHashEntry *he, int i, void *arg)
  223. {
  224.     XP_ASSERT(he->value);
  225.     if (he->value)
  226.         jss_DestroyTag((StyleTag *)he->value);
  227.  
  228.     return HT_ENUMERATE_NEXT;  /* keep enumerating */
  229. }
  230.  
  231. void PR_CALLBACK
  232. jss_FinalizeStyleObject(JSContext *mc, JSObject *obj)
  233. {
  234.     StyleObject *tags;
  235.  
  236.     XP_ASSERT(JS_InstanceOf(mc, obj, &Tags_class, 0) || JS_InstanceOf(mc, obj, &Classes_class, 0));
  237.  
  238.     /* Note: the prototype objects won't have any private data */
  239.     tags = JS_GetPrivate(mc, obj);
  240.     if (tags) {
  241.         XP_ASSERT(tags->type >= JSSTags && tags->type <= JSSClass);
  242.             
  243.         if (tags->table) {
  244.             if (tags->type != JSSClasses)
  245.                 PR_HashTableEnumerateEntries(tags->table, jss_DestroyTags, 0);
  246.             PR_HashTableDestroy(tags->table);
  247.         }
  248.     
  249.         if (tags->name)
  250.             XP_FREE(tags->name);
  251.  
  252.         XP_FREE(tags);
  253.     }
  254. }
  255.  
  256. /**** Implementation of JavaScript JSSTags classes ****/
  257.  
  258. /* Constructor for JSSTags class */
  259. JSBool PR_CALLBACK
  260. TagsConstructor (JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  261. {
  262.     /* Check arguments first */
  263.     XP_ASSERT(JS_InstanceOf(mc, obj, &Tags_class, 0));
  264.  
  265.     /* We don't expect any arguments */
  266.     XP_ASSERT(argc == 0);
  267.  
  268.     return JS_TRUE;
  269. }
  270.  
  271. /* Helper routine to determine the specificity of a tag */
  272. static uint32
  273. jss_TagSpecificity(StyleObject *obj)
  274. {
  275.     /* Compute the specificity for a tag of this type */
  276.     switch (obj->type) {
  277.         case JSSTags:
  278.             return JSS_SPECIFICITY(1, 0, 0);
  279.  
  280.         case JSSIds:
  281.             return JSS_SPECIFICITY(0, 0, 1);
  282.  
  283.         case JSSClasses:
  284.             XP_ASSERT(FALSE);
  285.             break;
  286.  
  287.         case JSSClass:
  288.             XP_ASSERT(obj->name);
  289.             if (obj->name && XP_STRCMP(obj->name, "all") == 0)
  290.                 return JSS_SPECIFICITY(0, 1, 0);
  291.             else
  292.                 return JSS_SPECIFICITY(1, 1, 0);
  293.             break;
  294.     }
  295.  
  296.     return 0;
  297. }
  298.  
  299. /* Called by JS to resolve names */
  300. JSBool PR_CALLBACK
  301. Tags_ResolveName(JSContext *mc, JSObject *obj, jsval id)
  302. {
  303.     if (JSVAL_IS_STRING(id)) {
  304.         char        *name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  305.          JSObject    *tag_obj;
  306.         StyleObject *tags;
  307.         StyleTag    *tag;
  308.  
  309.         XP_ASSERT(JS_InstanceOf(mc, obj, &Tags_class, 0));
  310.  
  311.            /* Get the pointer to the hash table */
  312.         tags = JS_GetPrivate(mc, obj);
  313.         if (!tags)
  314.             return JS_TRUE;  /* we must be getting called before we've been initialized */
  315.  
  316.         /*
  317.          * See if there is an existing StyleTag object with this name (all names
  318.          * are case insensitive like in CSS)
  319.          */
  320.         if (tags->table) {
  321.             PRHashEntry *he, **hep;
  322.             
  323.             hep = PR_HashTableRawLookup(tags->table, tags->table->keyHash(name), name);
  324.             if ((he = *hep) != 0) {
  325.                 /* Alias this name with the original name */
  326.                 JS_AliasProperty(mc, obj, (const char *)he->key, name);
  327.                 return JS_TRUE;
  328.             }
  329.         }
  330.         
  331.         /* Lazily create tag objects when needed */
  332.         tag_obj = JS_NewObject(mc, &Tag_class, 0, obj);
  333.         if (!tag_obj)
  334.             return JS_FALSE;
  335.         
  336.         /* Create a new StyleTag structure */
  337.         tag = jss_NewTag(name);
  338.         if (!tag) {
  339.             JS_ReportOutOfMemory(mc);
  340.             return JS_FALSE;
  341.         }
  342.         JS_SetPrivate(mc, tag_obj, tag);
  343.  
  344.         /* We don't create the hash table until it's actually needed */
  345.         if (!tags->table) {
  346.             /* All lookups must be case insensitive */
  347.             tags->table = PR_NewHashTable(8, (PRHashFunction)jss_HashStringNoCase,
  348.                 (PRHashComparator)jss_CompareStringsNoCase, PR_CompareValues, 0, 0);
  349.  
  350.             if (!tags->table) {
  351.                 jss_DestroyTag(tag);
  352.                 JS_ReportOutOfMemory(mc);
  353.                 return JS_FALSE;
  354.             }
  355.         }
  356.  
  357.         /* Add this tag to the hash table */
  358.         PR_HashTableAdd(tags->table, (const void *)tag->name, tag);
  359.         tag->specificity = jss_TagSpecificity(tags);
  360.  
  361.         /* Give the object a name */
  362.         return JS_DefineProperty(mc, obj, name, OBJECT_TO_JSVAL(tag_obj), 0, 0,
  363.             JSPROP_READONLY | JSPROP_PERMANENT);
  364.     }
  365.  
  366.     return JS_TRUE;    
  367. }
  368.  
  369. /**** Implementation of JavaScript JSSClasses class ****/
  370.  
  371. /* Constructor for JSSClasses class */
  372. JSBool PR_CALLBACK
  373. ClassesConstructor (JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  374. {
  375.     /* Check arguments first */
  376.     XP_ASSERT(JS_InstanceOf(mc, obj, &Classes_class, 0));
  377.  
  378.     /* We don't expect any arguments */
  379.     XP_ASSERT(argc == 0);
  380.  
  381.     return JS_TRUE;
  382. }
  383.  
  384. /* Called by JS to resolve names */
  385. JSBool PR_CALLBACK
  386. Classes_ResolveName(JSContext *mc, JSObject *obj, jsval id)
  387. {
  388.       if (JSVAL_IS_STRING(id)) {
  389.          char*         name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  390.         StyleObject *classes;
  391.         JSObject*     tags_obj;
  392.         StyleObject *tags;
  393.  
  394.         /* Get the pointer to the hash table */
  395.         classes = JS_GetPrivate(mc, obj);
  396.         if (!classes)
  397.             return JS_TRUE;  /* we must be getting called before we've been initialized */
  398.  
  399.         /*
  400.          * See if there is an existing StyleObject object with this name (all names
  401.          * are case insensitive like in CSS)
  402.          */
  403.         if (classes->table) {
  404.             PRHashEntry *he, **hep;
  405.             
  406.             hep = PR_HashTableRawLookup(classes->table, classes->table->keyHash(name), name);
  407.             if ((he = *hep) != 0) {
  408.                 /* Alias this name with the original name */
  409.                 JS_AliasProperty(mc, obj, (const char *)he->key, name);
  410.                 return JS_TRUE;
  411.             }
  412.         }
  413.         
  414.         /* Create an instance of the "Tags" class for the specified class name */
  415.         tags_obj = JS_NewObject(mc, &Tags_class, 0, obj);
  416.         if (!tags_obj) {
  417.             JS_ReportOutOfMemory(mc);
  418.             return JS_FALSE;
  419.         }
  420.  
  421.         /* Create a StyleObject data structure */
  422.         tags = (StyleObject *)XP_CALLOC(1, sizeof(StyleObject));
  423.         if (!tags) {
  424.             JS_ReportOutOfMemory(mc);
  425.             return JS_FALSE;
  426.         }
  427.  
  428.         /* Make a copy of the name */
  429.         tags->name = XP_STRDUP(name);
  430.         if (!tags->name) {
  431.             XP_FREE(tags);
  432.             JS_ReportOutOfMemory(mc);
  433.             return JS_FALSE;
  434.         }
  435.  
  436.         /* We don't create the hash table until it's actually needed */
  437.         if (!classes->table) {
  438.             /* All lookups must be case insensitive */
  439.             classes->table = PR_NewHashTable(8, (PRHashFunction)jss_HashStringNoCase,
  440.                 (PRHashComparator)jss_CompareStringsNoCase, PR_CompareValues, 0, 0);
  441.  
  442.             if (!classes->table) {
  443.                 if (tags->name)
  444.                     XP_FREE(tags->name);
  445.                 XP_FREE(tags);
  446.                 JS_ReportOutOfMemory(mc);
  447.                 return JS_FALSE;
  448.             }
  449.         }
  450.         
  451.         /* Add it to the hash table */
  452.         PR_HashTableAdd(classes->table, (const void *)tags->name, tags);
  453.         tags->type = JSSClass;
  454.         JS_SetPrivate(mc, tags_obj, tags);
  455.  
  456.         return JS_DefineProperty(mc, obj, name, OBJECT_TO_JSVAL(tags_obj), 0, 0,
  457.             JSPROP_READONLY | JSPROP_PERMANENT);
  458.     }
  459.  
  460.     return JS_TRUE;    
  461. }
  462.  
  463. /**** Implementation of JavaScript JSSTag class ****/
  464.  
  465. /* Constructor for JSSTag class */
  466. JSBool PR_CALLBACK
  467. TagConstructor (JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  468. {
  469.     /* Check arguments first */
  470.     XP_ASSERT(JS_InstanceOf(mc, obj, &Tag_class, 0));
  471.  
  472.     /* We don't expect any arguments */
  473.     XP_ASSERT(argc == 0);
  474.  
  475.     return JS_TRUE;
  476. }
  477.  
  478. /* Destroys a list of properties */
  479. void
  480. jss_DestroyProperties(StyleProperty *p)
  481. {
  482.     StyleProperty *next;
  483.  
  484.     while (p) {
  485.         next = p->next;
  486.  
  487.         /* Free the name */
  488.         if (p->name)
  489.             XP_FREE(p->name);
  490.  
  491.         /* If the value is a string then free it, too */
  492.         if (p->tag == JSVAL_STRING) {
  493.             XP_ASSERT(p->u.strVal);
  494.             if (p->u.strVal)
  495.                 XP_FREE(p->u.strVal);
  496.         }
  497.  
  498.         XP_FREE(p);
  499.         p = next;
  500.     }
  501. }
  502.  
  503. static JSBool
  504. jss_PropertySetValue(JSContext *mc, StyleProperty *prop, jsval *vp)
  505. {
  506.     JSString *str;
  507.  
  508.     if (JSVAL_IS_BOOLEAN(*vp)) {
  509.         prop->u.bVal = JSVAL_TO_BOOLEAN(*vp);
  510.  
  511.     } else if (JSVAL_IS_INT(*vp)) {
  512.         prop->u.nVal = JSVAL_TO_INT(*vp);
  513.         /* XXX - JSVAL_TAG doesn't do the right thing for ints */
  514.         prop->tag = JSVAL_INT;
  515.         return JS_TRUE;
  516.  
  517.     } else if (JSVAL_IS_DOUBLE(*vp)) {
  518.         prop->u.dVal = *JSVAL_TO_DOUBLE(*vp);
  519.  
  520.     } else {
  521.         XP_ASSERT(JSVAL_IS_STRING(*vp));
  522.         str = JSVAL_TO_STRING(*vp);
  523.         prop->u.strVal = XP_STRDUP(JS_GetStringBytes(str));
  524.     }
  525.     
  526.     prop->tag = JSVAL_TAG(*vp);
  527.     return JS_TRUE;
  528. }
  529.  
  530. static JSBool
  531. jss_PropertyGetValue(JSContext *mc, StyleProperty *prop, jsval *vp)
  532. {
  533.     switch (prop->tag) {
  534.         case JSVAL_BOOLEAN:
  535.             *vp = BOOLEAN_TO_JSVAL(prop->u.bVal);
  536.             break;
  537.  
  538.         case JSVAL_STRING:
  539.             *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(mc, prop->u.strVal));
  540.             break;
  541.  
  542.         case JSVAL_INT:
  543.             *vp = INT_TO_JSVAL(prop->u.nVal);
  544.             break;
  545.  
  546.         case JSVAL_DOUBLE:
  547.             JS_NewDoubleValue(mc, prop->u.dVal, vp);
  548.             break;
  549.  
  550.         default:
  551.             XP_ASSERT(FALSE);
  552.             break;
  553.     }
  554.  
  555.     return JS_TRUE;
  556. }
  557.  
  558. static StyleProperty *
  559. jss_TagGetProperty(JSContext *mc, StyleTag *tag, char *propname)
  560. {
  561.     StyleProperty *prop;
  562.  
  563.     /* Look for the property in our list of properties */
  564.     for (prop = tag->properties; prop; prop = prop->next) {
  565.         if (XP_STRCASECMP(prop->name, propname) == 0)
  566.             return prop;
  567.     }
  568.  
  569.     return 0;
  570. }
  571.  
  572. static JSBool
  573. jss_TagAddProperty(JSContext *mc, StyleTag *tag, char *name, jsval *vp)
  574. {
  575.     StyleProperty *prop;
  576.  
  577.     /* Create a new property */
  578.     prop = (StyleProperty *)XP_CALLOC(1, sizeof(StyleProperty));
  579.     if (!prop) {
  580.         JS_ReportOutOfMemory(mc);
  581.         return JS_FALSE;
  582.     }
  583.     
  584.     /* Set the name and property value */
  585.     prop->name = XP_STRDUP(name);
  586.     if (!prop->name) {
  587.         XP_FREE(prop);
  588.         JS_ReportOutOfMemory(mc);
  589.         return JS_FALSE;
  590.     }
  591.     jss_PropertySetValue(mc, prop, vp);
  592.  
  593.     /* Add it to the list */
  594.     prop->next = tag->properties;
  595.     tag->properties = prop;
  596.  
  597.     return JS_TRUE;
  598. }
  599.  
  600. /* Creates a new property if necessary */
  601. static JSBool
  602. jss_TagSetProperty(JSContext *mc, StyleTag *tag, char *name, jsval *vp)
  603. {
  604.     StyleProperty *prop;
  605.  
  606.     /* See if we already have the property defined */
  607.     prop = jss_TagGetProperty(mc, tag, name);
  608.  
  609.     if (prop)
  610.         jss_PropertySetValue(mc, prop, vp);
  611.     else
  612.         jss_TagAddProperty(mc, tag, name, vp);
  613.     
  614.     return JS_TRUE;
  615. }
  616.  
  617. /* Called by JS so we can handle the get operation */
  618. JSBool PR_CALLBACK
  619. Tag_GetProperty(JSContext *mc, JSObject *obj, jsval id, jsval *vp)
  620. {
  621.     if (JSVAL_IS_STRING(id)) {
  622.         char          *name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  623.         StyleTag       *tag = JS_GetPrivate(mc, obj);
  624.         StyleProperty *prop;
  625.     
  626.         XP_ASSERT(tag);
  627.         
  628.         LO_LockLayout();
  629.         prop = jss_TagGetProperty(mc, tag, name);
  630.         if (prop)
  631.             jss_PropertyGetValue(mc, prop, vp);
  632.         LO_UnlockLayout();
  633.     }
  634.     
  635.     return JS_TRUE;
  636. }
  637.  
  638. /* Called by JS so we can handle the set operation */
  639. JSBool PR_CALLBACK
  640. Tag_SetProperty(JSContext *mc, JSObject *obj, jsval id, jsval *vp)
  641. {
  642.     /* We only support number, strings, and booleans */
  643.     if (!JSVAL_IS_NUMBER(*vp) && !JSVAL_IS_BOOLEAN(*vp) && !JSVAL_IS_STRING(*vp))
  644.         return JS_TRUE;
  645.  
  646.     if (JSVAL_IS_STRING(id)) {
  647.         char          *name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  648.         StyleTag       *tag = JS_GetPrivate(mc, obj);
  649.  
  650.         XP_ASSERT(tag);
  651.     
  652.         if (tag) {
  653.             LO_LockLayout();
  654.             jss_TagSetProperty(mc, tag, name, vp);
  655.             LO_UnlockLayout();
  656.         }
  657.     }
  658.  
  659.     return JS_TRUE;
  660. }
  661.  
  662. /* Called by JS to resolve names */
  663. JSBool PR_CALLBACK
  664. Tag_ResolveName(JSContext *mc, JSObject *obj, jsval id)
  665. {
  666.       if (JSVAL_IS_STRING(id)) {
  667.               char   *name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  668.  
  669.               /*
  670.                * We need to have a resolve function for the case where there's a "with" clause, e.g.
  671.                * "with (tags.H1) {color = 'red'}". In this case we need to resolve the property or
  672.                * our setter function won't get called
  673.                */
  674.               if(is_valid_jsss_prop(name))
  675.                     return JS_DefineProperty(mc, obj, name, JSVAL_VOID, 0, 0,
  676.                       JSPROP_ENUMERATE | JSPROP_PERMANENT);
  677.       }
  678.  
  679.       return JS_TRUE; 
  680. }
  681.  
  682. /* Grouping syntax method for setting all the margins at once */
  683. JSBool PR_CALLBACK
  684. Tag_Margin(JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  685. {
  686.     StyleTag *tag = JS_GetPrivate(mc, obj);
  687.     
  688.     /*
  689.      * We expect between 1 and 4 arguments. We'll fail if there isn't at least
  690.      * one and silently ignore anything more than 4
  691.      */
  692.     if (argc == 0) {
  693.         JS_ReportError(mc, "Function margin() requires at least one argument.");
  694.         return JS_FALSE;
  695.     }
  696.  
  697.     XP_ASSERT(tag);
  698.  
  699.     /*
  700.      * The arguments apply to top, right, bottom, and left in that order. If there is
  701.      * only one argument it applies to all sides, if there are two or three, the missing
  702.      * values are taken from the opposite side
  703.      */
  704.     if (tag) {
  705.         jsval *vp;
  706.                 
  707.         LO_LockLayout();
  708.         jss_TagSetProperty(mc, tag, "marginTop", argv);
  709.         jss_TagSetProperty(mc, tag, "marginRight", argc >= 2 ? &argv[1] : argv);
  710.         jss_TagSetProperty(mc, tag, "marginBottom", argc >= 3 ? &argv[2] : argv);
  711.  
  712.         if (argc == 4)
  713.             vp = &argv[3];
  714.         else if (argc >= 2)
  715.             vp = &argv[1];
  716.         else
  717.             vp = argv;
  718.         jss_TagSetProperty(mc, tag, "marginLeft", vp);
  719.         LO_UnlockLayout();
  720.     }
  721.  
  722.     return JS_TRUE;
  723. }
  724.  
  725. /* Grouping syntax method for setting all the paddings at once */
  726. JSBool PR_CALLBACK
  727. Tag_Padding(JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  728. {
  729.     StyleTag *tag = JS_GetPrivate(mc, obj);
  730.     
  731.     /*
  732.      * We expect between 1 and 4 arguments. We'll fail if there isn't at least
  733.      * one and silently ignore anything more than 4
  734.      */
  735.     if (argc == 0) {
  736.         JS_ReportError(mc, "Function padding() requires at least one argument.");
  737.         return JS_FALSE;
  738.     }
  739.  
  740.     XP_ASSERT(tag);
  741.  
  742.     /*
  743.      * The arguments apply to top, right, bottom, and left in that order. If there is
  744.      * only one argument it applies to all sides, if there are two or three, the missing
  745.      * values are taken from the opposite side
  746.      */
  747.     if (tag) {
  748.         jsval *vp;
  749.         
  750.         LO_LockLayout();
  751.         jss_TagSetProperty(mc, tag, "paddingTop", argv);
  752.         jss_TagSetProperty(mc, tag, "paddingRight", argc >= 2 ? &argv[1] : argv);
  753.         jss_TagSetProperty(mc, tag, "paddingBottom", argc >= 3 ? &argv[2] : argv);
  754.         
  755.         if (argc == 4)
  756.             vp = &argv[3];
  757.         else if (argc >= 2)
  758.             vp = &argv[1];
  759.         else
  760.             vp = argv;
  761.         jss_TagSetProperty(mc, tag, "paddingLeft", vp);
  762.         LO_UnlockLayout();
  763.     }
  764.  
  765.     return JS_TRUE;
  766. }
  767.  
  768. /* Grouping syntax method for setting all the border widths at once */
  769. JSBool PR_CALLBACK
  770. Tag_BorderWidth(JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  771. {
  772.     StyleTag *tag = JS_GetPrivate(mc, obj);
  773.     
  774.     /*
  775.      * We expect between 1 and 4 arguments. We'll fail if there isn't at least
  776.      * one and silently ignore anything more than 4
  777.      */
  778.     if (argc == 0) {
  779.         JS_ReportError(mc, "Function borderWidth() requires at least one argument.");
  780.         return JS_FALSE;
  781.     }
  782.  
  783.     XP_ASSERT(tag);
  784.  
  785.     /*
  786.      * The arguments apply to top, right, bottom, and left in that order. If there is
  787.      * only one argument it applies to all sides, if there are two or three, the missing
  788.      * values are taken from the opposite side
  789.      */
  790.     if (tag) {
  791.         jsval *vp;
  792.  
  793.         LO_LockLayout();
  794.         jss_TagSetProperty(mc, tag, "borderTopWidth", argv);
  795.         jss_TagSetProperty(mc, tag, "borderRightWidth", argc >= 2 ? &argv[1] : argv);
  796.         jss_TagSetProperty(mc, tag, "borderBottomWidth", argc >= 3 ? &argv[2] : argv);
  797.  
  798.         if (argc == 4)
  799.             vp = &argv[3];
  800.         else if (argc >= 2)
  801.             vp = &argv[1];
  802.         else
  803.             vp = argv;
  804.         jss_TagSetProperty(mc, tag, "borderLeftWidth", vp);
  805.         LO_UnlockLayout();
  806.     }
  807.  
  808.     return JS_TRUE;
  809. }
  810.  
  811. /* Creates a new StyleTag structure */
  812. StyleTag *
  813. jss_NewTag(char *name)
  814. {
  815.     StyleTag *tag = (StyleTag *)XP_CALLOC(1, sizeof(StyleTag));
  816.  
  817.     if (!tag)
  818.         return 0;
  819.  
  820.     /* Make a copy of the name */
  821.     if (name) {
  822.         tag->name = XP_STRDUP(name);
  823.         if (!tag->name) {
  824.             XP_FREE(tag);
  825.             return 0;
  826.         }
  827.     }
  828.  
  829.     return tag;
  830. }
  831.  
  832. /* Destroys a StyleTag structure */
  833. void
  834. jss_DestroyTag(StyleTag *tag)
  835. {
  836.     if (tag->name)
  837.         XP_FREE(tag->name);
  838.     jss_DestroyProperties(tag->properties);
  839.     jss_DestroyRules(tag->rules);
  840.     XP_FREE(tag);
  841. }
  842.  
  843. /* JS function for creating contextual selectors */
  844. JSBool PR_CALLBACK
  845. jss_Contextual(JSContext *mc, JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
  846. {
  847.     /* We expect at least 1 argument */
  848.     if (argc == 0) {
  849.         JS_ReportError(mc, "Function contextual() requires at least one argument.");
  850.         return JS_FALSE;
  851.     }
  852.  
  853.     /* If there's just one argument, then this is really just a simple selector */
  854.     if (argc == 1) {
  855.         *rval = argv[0];  /* copy the argument to the result */
  856.         
  857.     } else {
  858.         StyleTag    *tag;
  859.         JSObject    *tag_obj;
  860.         StyleRule    *rule;
  861.         unsigned     i;
  862.  
  863.         /*
  864.          * Each StyleTag has a "rules" member where we store all contextual selectors that
  865.          * have that instance as the last simple selector in the list, i.e. the contextual
  866.          * selector "contextual(tags.UL, tags.OL)" would be stored in the "tags.OL" object
  867.          *
  868.          * Validate each of the arguments, and make sure it's a JavaScript object of
  869.          * type JSSTag
  870.          */
  871.         for (i = 0; i < argc; i++) {
  872.             /* We expect each simple selector to be a JavaScript object */
  873.             if (!JSVAL_IS_OBJECT(argv[i]) || !JS_InstanceOf(mc, (JSObject *)argv[i], &Tag_class, 0)) {
  874.                 /* XXX - don't report error! */
  875.                 JS_ReportError(mc, "Invalid argument number '%u' in call to contextual().", i);
  876.                 return JS_FALSE;
  877.             }
  878.         }
  879.  
  880.         tag = JS_GetPrivate(mc, (JSObject *)argv[argc - 1]);
  881.         XP_ASSERT(tag);
  882.  
  883.         LO_LockLayout();
  884.         
  885.         /* Look and see if there's already a rule for this selector */
  886.         rule = jss_LookupRule(mc, tag->rules, argc, argv);
  887.         if (!rule) {
  888.             /* Create a new rule */
  889.             rule = jss_NewRule(mc, argc, argv);
  890.             if (!rule) {
  891.                 LO_UnlockLayout();
  892.                 return JS_FALSE;
  893.             }
  894.  
  895.             /* Add it to the list of existing rules */
  896.             rule->next = tag->rules;
  897.             tag->rules = rule;
  898.         }
  899.         
  900.         LO_UnlockLayout();
  901.  
  902.         /* Create a new JavaScript object */
  903.         tag_obj = JS_NewObject(mc, &Tag_class, 0, 0);
  904.         if (!tag_obj) {
  905.             JS_ReportOutOfMemory(mc);
  906.             return JS_FALSE;
  907.         }
  908.         
  909.         JS_SetPrivate(mc, tag_obj, rule);
  910.         *rval = OBJECT_TO_JSVAL(tag_obj);
  911.     }
  912.  
  913.     return JS_TRUE;
  914. }
  915.  
  916. static void
  917. jss_TagAddStyleProperties(StyleTag *tag, StyleStruct *style)
  918. {
  919.     StyleProperty *prop;
  920.  
  921.     for (prop = tag->properties; prop; prop = prop->next) {
  922.         SS_Number *num;
  923.  
  924.         switch (prop->tag) {
  925.             case JSVAL_BOOLEAN:
  926.                 STYLESTRUCT_SetString(style, prop->name, prop->u.bVal ? "true" : "false",
  927.                     (int32)tag->specificity);
  928.                 break;
  929.  
  930.             case JSVAL_STRING:
  931.                 STYLESTRUCT_SetString(style, prop->name, prop->u.strVal,
  932.                     (int32)tag->specificity);
  933.                 break;
  934.  
  935.             case JSVAL_INT:
  936.                 num = STYLESTRUCT_NewSSNumber(style, (double)prop->u.nVal, "");
  937.                 STYLESTRUCT_SetNumber(style, prop->name, num, (int32)tag->specificity);
  938.                 STYLESTRUCT_FreeSSNumber(style, num);
  939.                 break;
  940.  
  941.             case JSVAL_DOUBLE:
  942.                 num = STYLESTRUCT_NewSSNumber(style, prop->u.dVal, "");
  943.                 STYLESTRUCT_SetNumber(style, prop->name, num, (int32)tag->specificity);
  944.                 STYLESTRUCT_FreeSSNumber(style, num);
  945.                 break;
  946.  
  947.             default:
  948.                 XP_ASSERT(FALSE);
  949.                 break;
  950.         }
  951.     }
  952. }
  953.  
  954. /*
  955.  * This routine will find all the selectors that apply and add their
  956.  * properties to the style struct
  957.  */
  958. static void
  959. jss_AddMatchingSelectors(JSSContext *jc, StyleTag *tag, StyleAndTagStack *styleStack)
  960. {
  961.     /*
  962.      * Iterate over each of the rules and for each one that applies add
  963.      * its properties
  964.      */
  965.     if (tag->rules) {
  966.         jss_EnumApplicableRules(jc, tag->rules, styleStack, (RULECALLBACK)jss_TagAddStyleProperties,
  967.             STYLESTACK_GetStyleByIndex(styleStack, 0));
  968.     }
  969. }
  970.  
  971. /*
  972.  * This routine is called to retrieve the list of style properties for the current
  973.  * tag (tag at the top of the tag stack)
  974.  */
  975. XP_Bool
  976. jss_GetStyleForTopTag(StyleAndTagStack *styleStack)
  977. {
  978.     TagStruct    *tag;
  979.     StyleStruct    *style;
  980.     JSSContext     jc;
  981.  
  982.     tag = STYLESTACK_GetTagByIndex(styleStack, 0);
  983.     style = STYLESTACK_GetStyleByIndex(styleStack, 0);
  984.     XP_ASSERT(tag && style);
  985.  
  986.     /* Get the top-level hash tables */
  987.     XP_MEMSET(&jc, 0, sizeof(JSSContext));
  988.     sml_GetJSSContext(styleStack, &jc);
  989.  
  990.     /*
  991.      * Find all the rules that apply and add their declarations. We pass
  992.      * the specificity along and the style stack decides whether to use the
  993.      * declaration. This way we avoid having to sort the declarations
  994.      *
  995.      * Start with document.ids
  996.      */
  997.     if (tag->id && jc.ids && jc.ids->table) {
  998.         StyleTag *jsstag = (StyleTag *)PR_HashTableLookup(jc.ids->table, tag->id);
  999.  
  1000.         if (jsstag) {
  1001.             jss_TagAddStyleProperties(jsstag, style);
  1002.             jss_AddMatchingSelectors(&jc, jsstag, styleStack);
  1003.         }
  1004.     }
  1005.  
  1006.     /* Now do document.classes */
  1007.     if (tag->class_name && jc.classes && jc.classes->table) {
  1008.         StyleObject *jsscls = (StyleObject *)PR_HashTableLookup(jc.classes->table, tag->class_name);
  1009.         
  1010.         if (jsscls && jsscls->table) {
  1011.             StyleTag *jsstag;
  1012.  
  1013.             /* First we check all elements of the class, e.g. classes.punk.all */
  1014.             jsstag = (StyleTag *)PR_HashTableLookup(jsscls->table, "all");
  1015.             if (jsstag) {
  1016.                 jss_TagAddStyleProperties(jsstag, style);
  1017.                 jss_AddMatchingSelectors(&jc, jsstag, styleStack);
  1018.             }
  1019.  
  1020.             /* Now check for the specified tag */
  1021.             jsstag = (StyleTag *)PR_HashTableLookup(jsscls->table, tag->name);
  1022.             if (jsstag) {
  1023.                 jss_TagAddStyleProperties(jsstag, style);
  1024.                 jss_AddMatchingSelectors(&jc, jsstag, styleStack);
  1025.             }
  1026.         }
  1027.     }
  1028.  
  1029.     /* Last we do document.tags */
  1030.     if (tag->name && jc.tags && jc.tags->table) {
  1031.         StyleTag *jsstag = (StyleTag *)PR_HashTableLookup(jc.tags->table, tag->name);
  1032.  
  1033.         if (jsstag) {
  1034.             jss_TagAddStyleProperties(jsstag, style);
  1035.             jss_AddMatchingSelectors(&jc, jsstag, styleStack);
  1036.         }
  1037.     }
  1038.  
  1039.     return JS_TRUE;
  1040. }
  1041.  
  1042. /* Grouping syntax methods */
  1043. static JSFunctionSpec tag_groupingMethods[] = {
  1044.     {"margins", Tag_Margin, 1},
  1045.     {"paddings", Tag_Padding, 1},
  1046.     {"borderWidths", Tag_BorderWidth, 1},
  1047.     {0}
  1048. };
  1049.  
  1050. /*
  1051.  * This routine is called to resolve names for the document object
  1052.  */
  1053. JSBool
  1054. JSS_ResolveDocName(JSContext *mc, MWContext *context, JSObject *obj, jsval id)
  1055. {
  1056.     if (JSVAL_IS_STRING(id)) {
  1057.         char *name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  1058.  
  1059.         if (XP_STRCMP(name, "tags") == 0 ||
  1060.             XP_STRCMP(name, "classes") == 0 ||
  1061.             XP_STRCMP(name, "ids") == 0 ||
  1062.             XP_STRCMP(name, "contextual") == 0) {
  1063.             JSObject *winobj = JS_GetParent(mc, obj);
  1064.  
  1065.             JSObject    *tags_obj, *classes_obj, *ids_obj;
  1066.             StyleObject *tags, *classes, *ids;
  1067.     
  1068.             /* Define the JS "JSSTags" class which is a container for "JSSTag" objects */
  1069.             if (!JS_InitClass(mc, winobj, NULL, &Tags_class, TagsConstructor, 2, 0, 0, 0, 0))
  1070.                 goto out_of_memory;
  1071.         
  1072.             /* Define an instances "tags" and bind it to the document */
  1073.             tags_obj = JS_DefineObject(mc, obj, "tags", &Tags_class, 0,
  1074.                 JSPROP_READONLY | JSPROP_PERMANENT);
  1075.             if (!tags_obj)
  1076.                 goto out_of_memory;
  1077.  
  1078.             /* Define an instances "ids" and bind it to the document */
  1079.             ids_obj = JS_DefineObject(mc, obj, "ids", &Tags_class, 0,
  1080.                 JSPROP_READONLY | JSPROP_PERMANENT);
  1081.             if (!ids_obj)
  1082.                 goto out_of_memory;
  1083.         
  1084.             /* Define the JS "JSSClasses" class which is a container for "JSSTags" objects */
  1085.             if (!JS_InitClass(mc, winobj, NULL, &Classes_class, ClassesConstructor, 2, 0, 0, 0, 0))
  1086.                 goto out_of_memory;
  1087.         
  1088.             /* Define an instances "classes" and bind it to the document */
  1089.             classes_obj = JS_DefineObject(mc, obj, "classes", &Classes_class, 0,
  1090.                 JSPROP_READONLY | JSPROP_PERMANENT);
  1091.             if (!classes_obj)
  1092.                 goto out_of_memory;
  1093.  
  1094.             /* Define our C data structures */
  1095.             tags = (StyleObject *)XP_CALLOC(1, sizeof(StyleObject));
  1096.             if (!tags)
  1097.                 goto out_of_memory;
  1098.             tags->type = JSSTags;
  1099.  
  1100.             classes = (StyleObject *)XP_CALLOC(1, sizeof(StyleObject));
  1101.             if (!classes)
  1102.                 goto out_of_memory;
  1103.             classes->type = JSSClasses;
  1104.  
  1105.             ids = (StyleObject *)XP_CALLOC(1, sizeof(StyleObject));
  1106.             if (!ids)
  1107.                 goto out_of_memory;
  1108.             ids->type = JSSIds;
  1109.  
  1110.             /* Add them to the style stack so we can get at them later */
  1111.             LO_SetStyleObjectRefs(context, tags, classes, ids);
  1112.  
  1113.             /* We need to be able to get to the C data structures from the JavaScript objects */
  1114.             JS_SetPrivate(mc, tags_obj, tags);
  1115.             JS_SetPrivate(mc, classes_obj, classes);
  1116.             JS_SetPrivate(mc, ids_obj, ids);
  1117.         
  1118.             /* Define the JS "JSSTag" class and some grouping syntax methods */
  1119.             if (!JS_InitClass(mc, winobj, NULL, &Tag_class, TagConstructor, 2, 0, tag_groupingMethods, 0, 0))
  1120.                 goto out_of_memory;
  1121.  
  1122.             /* Define the function for creating contextual selectors */
  1123.             JS_DefineFunction(mc, obj, "contextual", jss_Contextual, 1, 0);
  1124.         }
  1125.     }
  1126.  
  1127.     return JS_TRUE;
  1128.  
  1129.   out_of_memory:
  1130.     JS_ReportOutOfMemory(mc);
  1131.     return JS_FALSE;
  1132. }
  1133.  
  1134. /**** Definition of JavaScript classes ****/
  1135.  
  1136. /*
  1137.  * This is the JS "JSSTags" class. This class is a container for "JSSTag"
  1138.  * objects (see below). There are two main instance of this class: "document.tags"
  1139.  * and "document.ids"
  1140.  */
  1141. JSClass Tags_class = {
  1142.     "JSSTags", JSCLASS_HAS_PRIVATE,
  1143.     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  1144.     JS_EnumerateStub, Tags_ResolveName, JS_ConvertStub, jss_FinalizeStyleObject
  1145. };
  1146.  
  1147. /*
  1148.  * This is the JS "JSSClasses" class. This class is a container for "JSSTags"
  1149.  * objects which in turn contain the actual tags. There is one instance of this
  1150.  * class: "document.classes"
  1151.  */
  1152. JSClass Classes_class = {
  1153.     "JSSClasses", JSCLASS_HAS_PRIVATE,
  1154.     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  1155.     JS_EnumerateStub, Classes_ResolveName, JS_ConvertStub, jss_FinalizeStyleObject
  1156. };
  1157.  
  1158. /*
  1159.  * This is the JS "JSSTag" class. There can be as many instances of this class
  1160.  * as there are HTML tags. JSS declarations are represented as properties
  1161.  */
  1162. JSClass Tag_class = {
  1163.     "JSSTag", JSCLASS_HAS_PRIVATE,
  1164.     JS_PropertyStub, JS_PropertyStub, Tag_GetProperty, Tag_SetProperty,
  1165.     JS_EnumerateStub, Tag_ResolveName, JS_ConvertStub, JS_FinalizeStub
  1166. };
  1167.  
  1168.