Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. CSS engine
  2. ==========
  3.  
  4. Requirements
  5. ------------
  6.  
  7.  + Parse stylesheets conforming to the forward compatible CSS grammar
  8.    (Note that in the short term, the semantic analysis stage only need
  9.     support CSS2.1)
  10.  + Stylesheet management/merging (i.e. multiple stylesheets may be added
  11.    to a single engine context and thus affect style selection)
  12.  + Be able to select a style for a DOM node based upon the current stylesheets
  13.    in the engine context.
  14.  + Implemented as a standalone, reusable, library -- ideally MIT licensed.
  15.  
  16. Suggested API
  17. -------------
  18.  
  19. struct css_context;
  20. struct css_style;
  21. struct css_stylesheet;
  22.  
  23. typedef struct css_context css_context;
  24. typedef struct css_style css_style;
  25. typedef struct css_stylesheet css_stylesheet;
  26.  
  27. typedef enum css_error {
  28.         CSS_OK,
  29.         CSS_NOMEM,
  30.         /* etc */
  31. } css_error;
  32.  
  33. typedef enum css_origin {
  34.         CSS_ORIGIN_UA,
  35.         CSS_ORIGIN_USER,
  36.         CSS_ORIGIN_AUTHOR
  37. } css_origin;
  38.  
  39. #define CSS_MEDIA_SCREEN (1<<0)
  40. #define CSS_MEDIA_PRINT  (1<<1)
  41. /* etc */
  42. #define CSS_MEDIA_ALL    (0xffffffff)
  43.  
  44. #define CSS_PSEUDO_CLASS_NONE    (0)
  45. #define CSS_PSEUDO_CLASS_LINK    (1<<0)
  46. #define CSS_PSEUDO_CLASS_VISITED (1<<1)
  47. #define CSS_PSEUDO_CLASS_HOVER   (1<<2)
  48. #define CSS_PSEUDO_CLASS_ACTIVE  (1<<3)
  49. #define CSS_PSEUDO_CLASS_FOCUS   (1<<4)
  50.  
  51. typedef enum css_property {
  52.         CSS_BACKGROUND_ATTACHMENT,
  53.         /* etc */
  54. } css_property;
  55.  
  56. typedef struct css_value {
  57.         css_property property;
  58.  
  59.         union {
  60.                 css_background_attachment background_attachment;
  61.                 /* etc */
  62.         } value;
  63. } css_value;
  64.  
  65. typedef css_error (*css_import_handler)(void *pw, const char *url,
  66.                 css_stylesheet *sheet);
  67.  
  68. /* Initialise library */
  69. css_error css_init(void);
  70. /* Finalise library */
  71. css_error css_fini(void);
  72.  
  73. /* Create a stylesheet associated with the given URL,
  74.  * specifying the sheet's origin, the media type(s) it applies to and
  75.  * a callback routine for fetching imported sheets */
  76. css_stylesheet *css_stylesheet_create(const char *url,
  77.                 css_origin origin, uint32_t media,
  78.                 css_import_handler import_callback, void *pw);
  79. /* Destroy a stylesheet */
  80. void css_stylesheet_destroy(css_stylesheet *sheet);
  81.  
  82. /* Append data to a stylesheet, parsing progressively */
  83. css_error css_stylesheet_append_data(css_stylesheet *sheet,
  84.                 const uint8_t *data, size_t len);
  85. /* Tell stylesheet parser that there's no more data (will complete parsing) */
  86. css_error css_stylesheet_data_done(css_stylesheet *sheet);
  87.  
  88. /* Retrieve the URL associated with a stylesheet */
  89. const char *css_stylesheet_get_url(css_stylesheet *sheet);
  90. /* Retrieve the origin of a stylesheet */
  91. css_origin css_stylesheet_get_origin(css_stylesheet *sheet);
  92. /* Retrieve the media type(s) applicable to a stylesheet */
  93. uint32_t css_stylesheet_get_media(css_stylesheet *sheet);
  94.  
  95. /* Create a selection context */
  96. css_context *css_context_create(void);
  97. /* Destroy a selection context */
  98. void css_context_destroy(css_context *context);
  99.  
  100. /* Append a top-level stylesheet to a selection context */
  101. css_error css_context_append_sheet(css_context *context,
  102.                 css_stylesheet *sheet);
  103. /* Insert a top-level stylesheet into a selection context, at the given index */
  104. css_error css_context_insert_sheet(css_context *context,
  105.                 css_stylesheet *sheet, uint32_t index);
  106. /* Remove a top-level stylesheet from a selection context */
  107. css_error css_context_remove_sheet(css_context *context,
  108.                 css_stylesheet *sheet);
  109.  
  110. /* Retrieve the total number of top-level sheets in a selection context */
  111. uint32_t css_context_count_sheets(css_context *context);
  112. /* Get a stylesheet from a selection context given an index [0, count) */
  113. const css_stylesheet *css_context_get_sheet(css_context *context,
  114.                 uint32_t index);
  115.  
  116. /* Select a style for a given DOM node with the given pseudo classes active
  117.  * and media type.
  118.  *
  119.  * If the document language contains non-CSS presentational hints (e.g. HTML
  120.  * presentational attributes etc), then these are passed in through
  121.  * property_list and treated as if they were encountered at the start of the
  122.  * author stylesheet with a specificity of 0. */
  123. css_style *css_style_select(css_context *context,
  124.                 <dom_node_type> *node, uint32_t pseudo_classes, uint32_t media,
  125.                 css_value **property_list, uint32_t property_list_length);
  126. /* Destroy a selected style */
  127. void css_style_destroy(css_style *style);
  128.  
  129. /* Retrieve a property value from a style */
  130. css_value *css_value_get(css_style *style, css_property property);
  131. /* Destroy a property value */
  132. void css_value_destroy(css_value *value);
  133.  
  134. Memory management
  135. -----------------
  136.  
  137.  + Stylesheets are owned by their creator. Selection contexts reference them.
  138.  + Selection contexts are owned by the client.
  139.  + Selected styles are owned by the client.
  140.  + Property values are owned by the client.
  141.  
  142.  Therefore, the only difficulty lies within the handling of stylesheets
  143.  inserted into a selection context. The client code must ensure that a
  144.  stylesheet is destroyed after it has been removed from any selection
  145.  contexts which are using it.
  146.  
  147. DOM node types & tree traversal
  148. -------------------------------
  149.  
  150.  This is currently undecided. Either the CSS engine is tied to a DOM
  151.  implementation (and makes API calls directly), or it's more generic and
  152.  performs API calls through a vtable provided by the client.
  153.  
  154. Imported stylesheets
  155. --------------------
  156.  
  157.  Imported stylesheets are handled by the CSS engine creating an appropriate
  158.  css_stylesheet object for the imported sheet and then asking the client
  159.  to fetch the data and append it to the sheet. The imported sheet is then
  160.  stored in the sheet that imported it. This effectively creates a tree of
  161.  stylesheets beneath the initial top-level sheet created by the client.
  162.  
  163. Style selection algorithm
  164. -------------------------
  165.  
  166.  css_style_select(context, node, pseudo_classes, media,
  167.          property_list, property_list_length):
  168.    result = blank_style;
  169.    done_props = false;
  170.    foreach sheet in context:
  171.      # Assumes that sheets are in the order UA, USER, AUTHOR
  172.      if !done_props && css_stylesheet_get_origin(sheet) == CSS_ORIGIN_AUTHOR:
  173.        fake_rule = fake_rule(node, property_list, property_list_length);
  174.        cascade(result, fake_rule, CSS_ORIGIN_AUTHOR);
  175.        done_props = true;
  176.      process_sheet(sheet, node, pseudo_classes, media, result);
  177.    return result;
  178.  
  179.  fake_rule(node, property_list, property_list_length):
  180.    rule = (node.name, 0); # Specificity is 0
  181.    foreach (property, value, importance) in property_list:
  182.      rule[property] = (value, importance);
  183.    return rule;
  184.  
  185.  process_sheet(sheet, node, pseudo_classes, media, result):
  186.    if (css_stylesheet_get_media(sheet) & media) == 0:
  187.      return;
  188.    foreach import in sheet:
  189.      process_sheet(import, node, pseudo_classes, media, result);
  190.    origin = css_stylesheet_get_origin(sheet);
  191.    foreach rule in sheet:
  192.      if matches_rule(rule, node, pseudo_classes):
  193.        cascade(result, rule, origin);
  194.  
  195.  cascade(result, rule, origin):
  196.    foreach (property, value, importance) in rule:
  197.      insert = false;
  198.      if result[property]:
  199.        rOrigin = result[property].origin;
  200.        rImportance = result[property].importance;
  201.        rSpecificity = result[property].specificity;
  202.        if rOrigin < origin:
  203.          if rImportance == "important":
  204.            if rOrigin != CSS_ORIGIN_USER:
  205.              insert = true;
  206.          else:
  207.            insert = true;
  208.        else if rOrigin == origin:
  209.          if rImportance == "" && importance == "important":
  210.            if rOrigin == CSS_ORIGIN_UA:
  211.              if rSpecificity <= rule.specificity:
  212.                insert = true;
  213.            else:
  214.              insert = true;
  215.          else if rImportance == "important" && importance == "":
  216.            if rOrigin == CSS_ORIGIN_UA:
  217.              if rSpecificity <= rule.specificity:
  218.                insert = true;
  219.          else:
  220.            if rSpecificity <= rule.specificity:
  221.              insert = true;
  222.        else:
  223.          if origin == CSS_ORIGIN_USER && importance == "important":
  224.            insert = true;
  225.      else:
  226.        insert = true;
  227.      if insert:
  228.        result[property] = (value, origin, importance, rule.specificity);
  229.  
  230. Outstanding issues
  231. ------------------
  232.  
  233.   + Parsing/selection quirks.
  234.  
  235.     Probably as an argument to css_stylesheet_create() and possibly
  236.     css_style_select(). This could either take the form of a blanket
  237.     full/almost/not quirks mode flag or be more granular and permit the
  238.     toggling of individual quirks.
  239.  
  240.     References:
  241.  
  242.      + http://developer.mozilla.org/en/docs/Mozilla_Quirks_Mode_Behavior
  243.      + http://www.opera.com/docs/specs/doctype/
  244.      + http://www.quirksmode.org/css/quirksmode.html
  245.      + http://www.cs.tut.fi/~jkorpela/quirks-mode.html
  246.      + Grep WebKit sources for inCompatMode()
  247.  
  248.   + The :lang pseudo-class
  249.  
  250.     Need to pass the current language string into css_style_select()
  251.  
  252.   + Pseudo-elements
  253.  
  254.     Probably as an argument to css_style_select(). Most likely a bitfield
  255.     like the way in which pseudo-classes are handled.
  256.    
  257.     The inheritance model of :first-line and :first-letter is such that:
  258.  
  259.       + css_style_select() must begin with a blank style and not the
  260.         parent node's style
  261.       + an API for cascading one style onto another is needed
  262.  
  263.     This is because pseudo-elements may be nested inside children of the
  264.     node to which they are logically connected. e.g.:
  265.  
  266.       <div>
  267.        <p>
  268.         first paragraph
  269.        </p>
  270.       </div>
  271.    
  272.     is logically equivalent to
  273.  
  274.       <div>
  275.        <p>
  276.         <div:first-line>
  277.          <p:first-line>
  278.           first paragraph
  279.          </p:first-line>
  280.         </div:first-line>
  281.        </p>
  282.       </div>
  283.  
  284.     so the actual cascade order is only known at the time the render tree is
  285.     built. Note that, courtesy of scripting, the location of pseudo-elements
  286.     can move around (e.g. if some text was inserted just before the <p> within
  287.     the div, above, then <div:first-line> would move). Additionally, the actual
  288.     content that pseudo-elements apply to can change due to reflow.
  289.  
  290.     Pseudo-elements may also affect the processing of inline boxes. e.g.:
  291.  
  292.       <p>
  293.        <span>foo bar baz bat</span>
  294.       </p>
  295.  
  296.     becomes (logically):
  297.  
  298.       <p>
  299.        <p:first-line>
  300.         <span>foo bar baz </span>
  301.        </p:first-line>
  302.        <span>bat</span>
  303.       </p>
  304.  
  305.     In terms of interaction between pseudo-elements, :first-letter inherits
  306.     from :first-line e.g.:
  307.  
  308.       <p>
  309.        first line
  310.        second line
  311.       </p>
  312.  
  313.     becomes (logically):
  314.  
  315.       <p>
  316.        <p:first-line>
  317.         <p:first-letter>
  318.          f
  319.         </p:first-letter>
  320.         irst line
  321.        </p:first-line>
  322.        second line
  323.       </p>
  324.  
  325.     :first-line and :first-letter apply to the relevant content _including_ any
  326.     text inserted using :before and :after.
  327.  
  328.     List of CSS 3 pseudo-elements:
  329.  
  330.      + :(:)?first-line
  331.      + :(:)?first-letter
  332.      + :(:)?before
  333.      + :(:)?after
  334.      + ::selection
  335.      + ::footnote-call
  336.      + ::footnote-marker
  337.      + ::before-page-break
  338.      + ::after-page-break
  339.      + ::line-number-left
  340.      + ::line-number-right
  341.      + ::line-number-inside
  342.      + ::line-number-outside
  343.      + ::slot()
  344.      + ::value
  345.      + ::choices
  346.      + ::repeat-item
  347.      + ::repeat-index
  348.      + ::marker
  349.      + ::outside
  350.      + ::alternate
  351.      + ::line-marker
  352.  
  353.     References:
  354.  
  355.      + CSS 2.1 $$5.12 and $$12.1
  356.  
  357.   + Stylesheet charset handling
  358.  
  359.     An embedded stylesheet shares the charset of the containing document.
  360.  
  361.     The charset of a stand-alone stylesheet can be specified by (in order of
  362.     priority, highest -> lowest):
  363.    
  364.      + the transport layer
  365.      + a BOM and/or @charset at the immediate start of the sheet
  366.      + <link charset=""> or other metadata from the linking mechanism
  367.      + charset of referring stylesheet or document
  368.      + assuming UTF-8
  369.  
  370.     The API currently has no way of conveying the first, third, or fourth of
  371.     these to the engine. This can be realised through the addition of a
  372.     parameter to css_stylesheet_create()
  373.  
  374.     CSS 2.1 $4.4 specifies that a stylesheet's transport encoding must be a
  375.     superset of US-ASCII.
  376.  
  377.     The internal encoding will be UTF-8.
  378.  
  379.     All strings passed in by the client are assumed to be UTF-8 encoded.
  380.     Strings retrieved from DOM nodes are assumed to be UTF-8 encoded.
  381.  
  382.