Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 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
		 *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
      
267
       

268
        first paragraph
269
       

270
      
271
 
272
    is logically equivalent to
273
 
274
      
275
       

276
        
277
         
278
          first paragraph
279
         
280
        
281
       

282
      
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 

within

287
    the div, above, then  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
      

293
       foo bar baz bat
294
      

295
 
296
    becomes (logically):
297
 
298
      

299
       
300
        foo bar baz 
301
       
302
       bat
303
      

304
 
305
    In terms of interaction between pseudo-elements, :first-letter inherits
306
    from :first-line e.g.:
307
 
308
      

309
       first line
310
       second line
311
      

312
 
313
    becomes (logically):
314
 
315
      

316
       
317
        
318
         f
319
        
320
        irst line
321
       
322
       second line
323
      

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
     +  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