2 * Copyright (C) 2005-2008 Atsushi Konno All rights reserved.
3 * Copyright (C) 2005 QSDN,Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include "chxj_serf.h"
19 #include "chxj_encoding.h"
20 #include "qs_parse_string.h"
21 #include "apr_pools.h"
27 #define list_insert(node, point) do { \
28 node->ref = point->ref; \
31 point->ref = &node->next; \
34 #define list_remove(node) do { \
35 *node->ref = node->next; \
36 node->next->ref = node->ref; \
39 /*===========================================================================*/
41 /*===========================================================================*/
42 struct css_already_import_stack {
44 struct css_already_import_stack *next;
45 struct css_already_import_stack **ref;
49 css_stylesheet_t *stylesheet;
55 css_property_t property_head;
56 struct css_already_import_stack imported_stack_head;
58 static char *s_css_parser_get_charset(apr_pool_t *pool, const char *src, apr_size_t *next_pos);
59 static void s_css_parser_from_uri_start_selector(CRDocHandler * a_this, CRSelector *a_selector_list);
60 static void s_css_parser_from_uri_end_selector(CRDocHandler * a_this, CRSelector *a_selector_list);
61 static void s_css_parser_from_uri_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important);
62 static css_property_t *s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from);
63 static css_selector_t *s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name);
64 static css_selector_t *s_search_selector(css_stylesheet_t *stylesheet, const char *name);
65 static void s_merge_property(css_selector_t *sel, css_property_t *tgt);
66 static void s_css_parser_from_uri_import_style(CRDocHandler *a_this, GList *a_media_list, CRString *a_uri, CRString *a_uri_default_ns, CRParsingLocation *a_location);
67 static char *s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri);
68 static char *s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool);
69 static css_stylesheet_t *s_chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *uri);
70 static int s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url);
71 static css_stylesheet_t *s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet);
72 static void s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack);
75 chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *uri)
77 css_stylesheet_t *new_stylesheet = s_chxj_css_parse_from_uri(r, pool, NULL, old_stylesheet, uri);
78 chxj_css_stylesheet_dump(new_stylesheet);
79 return new_stylesheet;
82 static css_stylesheet_t *
83 s_chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *uri)
85 CRParser *parser = NULL;
86 CRDocHandler *sac_handler = NULL;
90 char *full_url = NULL;
93 css_stylesheet_t *stylesheet = NULL;
94 struct css_already_import_stack *new_stack;
95 struct css_app_data app_data;
99 DBG(r, "start chxj_css_parse_from_uri() uri:[%s]", uri);
101 base_url = s_uri_to_base_url(&r->parsed_uri, pool);
102 full_url = s_path_to_fullurl(pool, base_url, r->parsed_uri.path, uri);
104 /* check already import */
105 if (imported_stack && s_is_already_imported(imported_stack, full_url)) {
106 DBG(r, "end chxj_css_parse_from_uri(): already imported:[%s]", full_url);
111 css = chxj_serf_get(r, pool, full_url);
113 ERR(r, "%s:%d end chxj_css_parse_from_uri(): serf_get failed: url:[%s]", APLOG_MARK, uri);
116 srclen = strlen(css);
118 /* convert encoding */
119 charset = s_css_parser_get_charset(pool, css, &next_pos);
121 DBG(r, "charset:[%s]\n", charset);
123 srclen = strlen(css);
124 css = chxj_iconv(r, pool, css, &srclen, charset, "UTF-8");
128 parser = cr_parser_new_from_buf((guchar *)css, srclen, CR_UTF_8, FALSE);
130 ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_parser_new_from_buf() failed", APLOG_MARK);
133 sac_handler = cr_doc_handler_new();
135 ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_doc_handler_new() failed", APLOG_MARK);
136 cr_parser_destroy(parser);
140 stylesheet = apr_palloc(pool, sizeof(*stylesheet));
141 memset(stylesheet, 0, sizeof(*stylesheet));
142 stylesheet->selector_head.next = &stylesheet->selector_head;
143 stylesheet->selector_head.ref = &stylesheet->selector_head.next;
145 memset(&app_data, 0, sizeof(struct css_app_data));
146 app_data.stylesheet = stylesheet;
147 app_data.selector_list = NULL;
148 app_data.selector_count = 0;
149 app_data.pool = pool;
150 app_data.error_occured = 0;
152 if (imported_stack) {
153 s_copy_already_import_stack(pool, &app_data.imported_stack_head, imported_stack);
156 app_data.imported_stack_head.next = &app_data.imported_stack_head;
157 app_data.imported_stack_head.ref = &app_data.imported_stack_head.next;
159 new_stack = apr_palloc(pool, sizeof(*new_stack));
160 memset(new_stack, 0, sizeof(*new_stack));
161 new_stack->next = new_stack;
162 new_stack->ref = &new_stack->next;
163 new_stack->full_url = full_url;
164 list_insert(new_stack, (&app_data.imported_stack_head));
166 sac_handler->app_data = &app_data;
168 sac_handler->start_selector = s_css_parser_from_uri_start_selector;
169 sac_handler->end_selector = s_css_parser_from_uri_end_selector;
170 sac_handler->property = s_css_parser_from_uri_property;
171 sac_handler->import_style = s_css_parser_from_uri_import_style;
173 ret = cr_parser_set_sac_handler(parser, sac_handler);
175 ERR(r, "%s:%d end chxj_css_parse_from_uri(): cr_parser_set_sac_handler() failed: ret:[%d]", APLOG_MARK, ret);
176 cr_parser_destroy(parser);
180 ret = cr_parser_parse(parser);
181 cr_parser_destroy(parser);
182 DBG(r, "end chxj_css_parse_from_uri() url:[%s]", uri);
183 return s_merge_stylesheet(pool, old_stylesheet, app_data.stylesheet);
187 #define ERROR_OCCORED do { \
188 if (app_data->error_occured) { \
196 struct css_app_data *app_data = (struct css_app_data *)a_this->app_data
201 s_css_parser_from_uri_start_selector(CRDocHandler * a_this, CRSelector *a_selector_list)
204 CRSelector *cur = NULL;
208 app_data->selector_count = 0;
209 for (cur = a_selector_list; cur; cur = cur->next)
210 app_data->selector_count++;
212 app_data->selector_list = apr_palloc(app_data->pool, sizeof(char *) * app_data->selector_count);
213 if (! app_data->selector_list) {
214 ERR(app_data->r, "%s:%d Out of memory", APLOG_MARK);
215 app_data->error_occured = 1;
220 for (cur = a_selector_list; cur; cur = cur->next) {
221 if (cur->simple_sel) {
222 guchar *tmp_str = cr_simple_sel_to_string(cur->simple_sel);
224 app_data->selector_list[ii++] = apr_pstrdup(app_data->pool, (char *)tmp_str);
230 app_data->property_head.next = &app_data->property_head;
231 app_data->property_head.ref = &app_data->property_head.next;
236 s_css_parser_from_uri_end_selector(CRDocHandler * a_this, CRSelector *a_selector_list)
239 css_property_t *cur = NULL;
243 if (app_data->property_head.next) {
244 for (ii=0; ii<app_data->selector_count; ii++) {
245 css_selector_t *sel = s_new_selector(app_data->pool, app_data->stylesheet, app_data->selector_list[ii]);
247 for (cur = app_data->property_head.next; cur && cur != &app_data->property_head; cur = cur->next) {
248 css_property_t *tgt = s_css_parser_copy_property(app_data->pool, cur);
249 css_property_t *pnt = &sel->property_head;
250 s_merge_property(sel, tgt);
252 css_selector_t *point_selector = &app_data->stylesheet->selector_head;
253 list_insert(sel, point_selector);
256 app_data->property_head.next = &app_data->property_head;
257 app_data->property_head.ref = &app_data->property_head.next;
261 s_merge_property(css_selector_t *sel, css_property_t *tgt)
264 css_property_t *pnt = &sel->property_head;
265 char l = tolower(*tgt->name);
266 char u = toupper(*tgt->name);
267 for (cur = pnt->next; cur != pnt;cur = cur->next) {
268 if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, tgt->name) == 0) {
269 cur->value = tgt->value;
273 list_insert(tgt, pnt);
276 static css_selector_t *
277 s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name)
279 css_selector_t *sel = NULL;
280 sel = s_search_selector(stylesheet, name);
284 sel->ref = &sel->next;
287 sel = apr_palloc(pool, sizeof(css_selector_t));
288 memset(sel, 0, sizeof(css_selector_t));
291 sel->ref = &sel->next;
292 sel->property_head.next = &sel->property_head;
293 sel->property_head.ref = &sel->property_head.next;
298 static css_selector_t *
299 s_search_selector(css_stylesheet_t *stylesheet, const char *name)
302 char l = tolower(*name);
303 char u = toupper(*name);
304 if (! stylesheet) return NULL;
305 for (cur = stylesheet->selector_head.next; cur != &stylesheet->selector_head; cur = cur->next) {
306 if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, name) == 0) {
313 static css_property_t *
314 s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from)
316 css_property_t *prop = apr_palloc(pool, sizeof(css_property_t));
317 prop->name = apr_pstrdup(pool, from->name);
318 prop->value = apr_pstrdup(pool, from->value);
320 prop->ref = &prop->next;
328 s_css_parser_from_uri_property(CRDocHandler *a_this, CRString *a_name, CRTerm *a_expression, gboolean a_is_important)
332 css_property_t *property;
334 if (a_name && a_expression) {
336 property = apr_palloc(app_data->pool, sizeof(*property));
337 memset(property, 0, sizeof(*property));
338 property->name = apr_pstrdup(app_data->pool, cr_string_peek_raw_str(a_name));
339 tmp_str = cr_term_one_to_string(a_expression);
340 property->value = apr_pstrdup(app_data->pool, (char *)tmp_str);
344 css_property_t *point_property = &app_data->property_head;
345 list_insert(property, point_property);
351 s_css_parser_get_charset(apr_pool_t *pool, const char *src, apr_size_t *next_pos)
353 register char *p = (char *)src;
361 for (; *p && is_white_space(*p); p++)
364 #define CUT_TOKEN(X) \
367 for (;*p && (X); p++) \
369 ret = apr_palloc(pool, p - sv + 1); \
370 memset(ret, 0, p - sv + 1); \
371 memcpy(ret, sv, p - sv); \
376 if (strncasecmp(p, "@charset", sizeof("@charset")-1) == 0) {
377 p += sizeof("@charset");
378 for (; *p && is_white_space(*p); p++)
381 CUT_TOKEN(*p != '"');
382 if (! *p) return NULL;
384 else if (*p == '\'') {
385 CUT_TOKEN(*p != '\'');
386 if (! *p) return NULL;
389 CUT_TOKEN(! is_white_space(*p));
390 if (! *p) return NULL;
395 *next_pos = p - src + 1;
406 s_css_parser_from_uri_import_style(CRDocHandler *a_this, GList *a_media_list, CRString *a_uri, CRString *a_uri_default_ns, CRParsingLocation *a_location)
411 guint len = g_list_length(a_media_list);
413 css_stylesheet_t *new_stylesheet = NULL;
415 for (ii=0; ii<len; ii++) {
416 char *str = cr_string_peek_raw_str(g_list_nth_data(a_media_list, ii));
417 if (('h' == *str || 'H' == *str) && strcasecmp(str, "handheld") == 0) {
421 if (('a' == *str || 'A' == *str) && strcasecmp(str, "all") == 0) {
426 if (flag || len == 0) {
429 char *new_url = NULL;
430 char *import_url = cr_string_peek_raw_str(a_uri);
431 char *base_url = NULL;
433 base_url = s_uri_to_base_url(&app_data->r->parsed_uri, app_data->pool);
434 new_url = s_path_to_fullurl(app_data->pool, base_url, app_data->r->parsed_uri.path, import_url);
436 new_stylesheet = s_chxj_css_parse_from_uri(app_data->r, app_data->pool, &app_data->imported_stack_head, app_data->stylesheet, new_url);
437 if (new_stylesheet) {
438 app_data->stylesheet = new_stylesheet;
446 s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri)
448 char *new_url = NULL;
449 if (chxj_starts_with(uri, "http")) {
454 return apr_pstrcat(pool, base_url, uri, NULL);
457 new_url = apr_pstrcat(pool, base_url, base_path, NULL);
458 if (new_url[strlen(new_url)-1] == '/') {
459 new_url = apr_pstrcat(pool, new_url, uri, NULL);
462 new_url = apr_pstrcat(pool, new_url, "/", uri, NULL);
469 s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool)
471 char *new_url = apr_psprintf(pool, "%s://%s", uri->scheme, uri->hostname);
472 if (strcmp(uri->scheme, "http") == 0) {
473 if (uri->port != 80 && uri->port != 0) {
474 new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
477 else if (strcmp(uri->scheme, "https") == 0) {
478 if (uri->port != 443 && uri->port != 0) {
479 new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
486 s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url)
488 struct css_already_import_stack *cur;
489 char l = tolower(*url);
490 char u = toupper(*url);
491 for (cur = imported_stack_head->next; cur != imported_stack_head; cur = cur->next) {
492 if ((l == *cur->full_url || u == *cur->full_url) && strcasecmp(url, cur->full_url) == 0) {
499 static css_stylesheet_t *
500 s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet)
503 if (! old_stylesheet) {
504 return new_stylesheet;
507 for (cur = new_stylesheet->selector_head.next; cur != &new_stylesheet->selector_head; cur = cur->next) {
508 char *name = cur->name;
509 char l = tolower(*name);
510 char u = toupper(*name);
511 css_selector_t *cur_old;
515 for (cur_old = old_stylesheet->selector_head.next; cur_old != &old_stylesheet->selector_head; cur_old = cur_old->next) {
516 char *oldname = cur_old->name;
517 if ((l == *oldname || u == *oldname) && strcasecmp(name, oldname) == 0) {
518 css_property_t *cur_prop;
519 for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
520 css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
521 s_merge_property(cur_old, target);
528 /* add new selector */
529 css_property_t *cur_prop;
530 css_selector_t *new_selector = apr_palloc(pool, sizeof(*new_selector));
531 memset(new_selector, 0, sizeof(*new_selector));
532 new_selector->next = new_selector;
533 new_selector->ref = &new_selector->next;
534 new_selector->property_head.next = &new_selector->property_head;
535 new_selector->property_head.ref = &new_selector->property_head.next;
536 new_selector->name = apr_pstrdup(pool, name);
537 for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
538 css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
539 list_insert(target, (&new_selector->property_head));
541 list_insert(new_selector, (&old_stylesheet->selector_head));
545 return old_stylesheet;
550 s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack)
552 struct css_already_import_stack *cur;
555 base->ref = &base->next;
557 for (cur = imported_stack->next; cur != imported_stack; cur = cur->next) {
558 struct css_already_import_stack *new_stack;
559 new_stack = apr_palloc(pool, sizeof(*new_stack));
560 memset(new_stack, 0, sizeof(*new_stack));
561 new_stack->full_url = apr_pstrdup(pool, cur->full_url);
562 list_insert(new_stack, base);
568 chxj_css_stylesheet_dump(css_stylesheet_t *stylesheet)
570 css_selector_t *cur_sel;
571 css_property_t *cur_prop;
573 for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
574 fprintf(stderr, "selector:[%s]\n", cur_sel->name);
575 for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
576 fprintf(stderr, "\tproperty:\n");
577 fprintf(stderr, "\t\t- name:%s\n", cur_prop->name);
578 fprintf(stderr, "\t\t- value:%s\n", cur_prop->value);
585 chxj_css_parse_from_style_tag(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *style_value)
591 chxj_css_parse_from_style_attribute(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *style_attribute_value)
594 /*===========================================================================*/
596 /*===========================================================================*/
598 chxj_css_find_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, const char *tag_name, const char *class_name, const char *id)
602 /*===========================================================================*/
603 /* push/pop current_stylesheet_stack */
604 /*===========================================================================*/