2 * Copyright (C) 2005-2011 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 "chxj_str_util.h"
21 #include "qs_parse_string.h"
22 #include "apr_pools.h"
27 #if defined(CHXJ_TEST)
34 #define list_insert(node, point) do { \
35 node->ref = point->ref; \
38 point->ref = &node->next; \
41 #define list_remove(node) do { \
42 *node->ref = node->next; \
43 node->next->ref = node->ref; \
46 /*===========================================================================*/
48 /*===========================================================================*/
49 struct css_already_import_stack {
51 struct css_already_import_stack *next;
52 struct css_already_import_stack **ref;
56 css_stylesheet_t *stylesheet;
62 css_property_t property_head;
63 struct css_already_import_stack imported_stack_head;
66 /*===========================================================================*/
70 /*===========================================================================*/
71 static void s_css_parser_from_uri_start_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors);
72 static void s_css_parser_from_uri_end_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors);
73 static void s_css_parser_from_uri_property(SCSSParserPtr_t parser, const char *propertyName, const char *value, int impotant);
74 static css_property_t *s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from);
75 static css_selector_t *s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name);
76 static css_selector_t *s_search_selector(css_stylesheet_t *stylesheet, const char *name);
77 static void s_merge_property(css_selector_t *sel, css_property_t *tgt);
78 static void s_css_parser_from_uri_import_style(SCSSParserPtr_t parser, const char *uri, const char **media, const char *defaultNamespaceURI);
79 static char *s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri);
80 static char *s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool);
81 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);
82 static int s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url);
83 static css_stylesheet_t *s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet);
84 static void s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack);
85 static css_selector_t *s_search_selector_regexp(Doc *doc, request_rec *r, apr_pool_t *pool, css_stylesheet_t *stylesheet, const char *pattern_str1, const char *pattern_str2, Node *node);
86 static void s_get_tag_and_class_and_id(Doc *doc, Node *node, char **tag_name, char **class_name, char **id);
87 static char *s_cmp_now_node_vs_current_style(Doc *doc, request_rec *r, apr_pool_t *pool, char *src, ap_regex_t *pattern4, Node *node);
88 static css_stylesheet_t *s_dup_stylesheet(Doc *doc, css_stylesheet_t *org);
92 * Data is acquired from url specified by using libserf.
93 * The acquired data is analyzed in the syntax and css_stylesheet_t is made.
95 * @param r request_rec
97 * @param old_stylesheet old stylesheet
100 * @return css_stylesheet_t
103 chxj_css_parse_from_uri(request_rec *r, apr_pool_t *pool, css_stylesheet_t *old_stylesheet, const char *uri)
105 return s_chxj_css_parse_from_uri(r, pool, NULL, old_stylesheet, uri);
109 * find selector engine.
110 * @param doc Doc structure.
111 * @param stylesheet Retrieval object.
112 * @param node this node.
113 * @return css_selector_t if any. null if not found.
117 chxj_css_find_selector(Doc *doc, css_stylesheet_t *stylesheet, Node *node)
119 request_rec *r = doc->r;
120 apr_pool_t *pool = doc->pool;
121 css_selector_t *sel = NULL;
122 char *tag_name = NULL;
123 char *class_name = NULL;
125 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
131 s_get_tag_and_class_and_id(doc, node, &tag_name, &class_name, &id);
133 if (! tag_name || strcasecmp("ROOT", tag_name) == 0) {
134 ERR(r,"REQ[%X] %s:%d tag_name is null", TO_ADDR(r),APLOG_MARK);
137 char *pattern_str1 = NULL;
138 char *pattern_str2 = NULL;
139 if (class_name && id) {
140 pattern_str1 = apr_psprintf(pool,
141 "^((%s|\\*)(\\.%s)?(#%s)?|(\\*|)\\.%s(#%s)?|(\\*|)(\\.%s)?#%s)$",
149 pattern_str2 = apr_psprintf(pool,
150 ".*([ >+])((%s|\\*)(\\.%s)?(#%s)?|(\\*|)\\.%s(#%s)?|(\\*|)(\\.%s)?#%s)$",
158 sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
160 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
165 pattern_str1 = apr_psprintf(pool,
166 "^((%s|\\*)(#%s)?|(\\*|)(#%s)?|(\\*|)#%s)$",
171 pattern_str2 = apr_psprintf(pool,
172 ".*([ >+])((%s|\\*)(#%s)?|(\\*|)(#%s)?|(\\*|)#%s)$",
177 sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
179 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
183 else if (class_name) {
184 pattern_str1 = apr_psprintf(pool,
185 "^((%s|\\*)(\\.%s)?|(\\*|)\\.%s|(\\*|)(\\.%s))$",
190 pattern_str2 = apr_psprintf(pool,
191 ".*([ >+])((%s|\\*)(\\.%s)?|(\\*|)\\.%s|(\\*|)(\\.%s)?)$",
196 sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
198 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
203 pattern_str1 = apr_psprintf(pool,
206 pattern_str2 = apr_psprintf(pool,
207 ".*([ >+])(%s|\\*)$",
209 sel = s_search_selector_regexp(doc, r, pool, stylesheet, pattern_str1, pattern_str2, node);
211 DBG(r,"REQ[%X] (FOUND)",TO_ADDR(r));
212 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
217 DBG(r,"REQ[%X] (Not FOUND)",TO_ADDR(r));
218 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
222 /*===========================================================================*/
226 /*===========================================================================*/
227 static css_selector_t *
228 s_search_selector_regexp(Doc *doc, request_rec *r, apr_pool_t *pool, css_stylesheet_t *stylesheet, const char *pattern_str1, const char *pattern_str2, Node *node)
230 Node *node_sv = node;
231 css_selector_t *ret_sel = NULL;
232 css_selector_t *tail;
234 ap_regex_t *pattern1 = chxj_ap_pregcomp(pool, pattern_str1, AP_REG_EXTENDED|AP_REG_ICASE);
235 ap_regex_t *pattern2 = chxj_ap_pregcomp(pool, pattern_str2, AP_REG_EXTENDED|AP_REG_ICASE);
236 ap_regex_t *pattern3 = chxj_ap_pregcomp(pool, "^.*([>+ ])([^>+ ]+?)$", AP_REG_EXTENDED|AP_REG_ICASE);
237 ap_regex_t *pattern4 = chxj_ap_pregcomp(pool, "^([^.#]+?)?(\\.[^#]+?)?(#.+?)?$", AP_REG_EXTENDED|AP_REG_ICASE);
239 tail = (css_selector_t *)((apr_size_t)stylesheet->selector_head.ref - (apr_size_t)APR_OFFSETOF(css_selector_t, next));
241 cur != &stylesheet->selector_head;
242 cur = (css_selector_t *)((apr_size_t)cur->ref - (apr_size_t)APR_OFFSETOF(css_selector_t, next))) {
243 ap_regmatch_t match[256];
244 if (chxj_ap_regexec(pattern1, cur->name, pattern1->re_nsub + 1, match, 0) == 0) {
249 if (chxj_ap_regexec(pattern2, cur->name, pattern2->re_nsub + 1, match, 0) == 0) {
250 char *src = apr_pstrdup(pool, cur->name);
251 char *one = chxj_ap_pregsub(pool, "$1",src, pattern2->re_nsub + 1, match);
254 *strrchr(src, *one) = 0;
256 case '>': /* Child selectors */
257 if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
258 one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
260 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->parent);
267 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->parent);
277 case '+': /* Adjacent sibling selectors */
278 if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
279 one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
280 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->prev);
292 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->prev);
301 case ' ': /* Descendant selectors */
302 if (chxj_ap_regexec(pattern3, src, pattern3->re_nsub + 1, match, 0) == 0) {
303 one = chxj_ap_pregsub(pool, "$1",src, pattern3->re_nsub + 1, match);
304 for (; node && node->parent; node = node->parent) {
305 if (strcasecmp(node->name, "ROOT") == 0 || strcasecmp(node->parent->name, "ROOT") == 0) {
309 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, strrchr(src, *one)+1, pattern4, node->parent);
318 for (; node && node->parent; node = node->parent) {
319 if (strcasecmp(node->name, "ROOT") == 0 || strcasecmp(node->parent->name, "ROOT") == 0) {
323 char *ret = s_cmp_now_node_vs_current_style(doc, r, pool, src, pattern4, node->parent);
340 chxj_ap_pregfree(pool, pattern1);
341 chxj_ap_pregfree(pool, pattern2);
342 chxj_ap_pregfree(pool, pattern3);
343 chxj_ap_pregfree(pool, pattern4);
346 static css_stylesheet_t *
347 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)
349 SCSSParserPtr_t parser = NULL;
350 SCSSSACHandlerPtr_t handler = NULL;
352 char *full_url = NULL;
354 css_stylesheet_t *stylesheet = NULL;
355 struct css_already_import_stack *new_stack;
356 struct css_app_data app_data;
361 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
362 DBG(r,"REQ[%X] uri:[%s]", TO_ADDR(r),uri);
364 base_url = s_uri_to_base_url(&r->parsed_uri, pool);
365 full_url = s_path_to_fullurl(pool, base_url, r->parsed_uri.path, uri);
367 /* check already import */
368 if (imported_stack && s_is_already_imported(imported_stack, full_url)) {
369 DBG(r,"REQ[%X] already imported:[%s]", TO_ADDR(r),full_url);
370 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
375 css = chxj_serf_get(r, pool, full_url, 0, &css_len);
377 ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): serf_get failed: url:[%s]", TO_ADDR(r),APLOG_MARK, uri);
378 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
381 srclen = strlen(css);
384 parser = scss_parser_new_from_buf(pool, css, "");
386 ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): cr_parser_new_from_buf() failed", TO_ADDR(r),APLOG_MARK);
391 handler = scss_doc_handler_new(parser);
393 ERR(r, "REQ[%X] %s:%d end chxj_css_parse_from_uri(): cr_doc_handler_new() failed", TO_ADDR(r),APLOG_MARK);
397 stylesheet = apr_palloc(pool, sizeof(*stylesheet));
398 memset(stylesheet, 0, sizeof(*stylesheet));
399 stylesheet->selector_head.next = &stylesheet->selector_head;
400 stylesheet->selector_head.ref = &stylesheet->selector_head.next;
402 memset(&app_data, 0, sizeof(struct css_app_data));
403 app_data.stylesheet = stylesheet;
404 app_data.selector_list = NULL;
405 app_data.selector_count = 0;
406 app_data.pool = pool;
407 app_data.error_occured = 0;
409 if (imported_stack) {
410 s_copy_already_import_stack(pool, &app_data.imported_stack_head, imported_stack);
413 app_data.imported_stack_head.next = &app_data.imported_stack_head;
414 app_data.imported_stack_head.ref = &app_data.imported_stack_head.next;
416 new_stack = apr_palloc(pool, sizeof(*new_stack));
417 memset(new_stack, 0, sizeof(*new_stack));
418 new_stack->next = new_stack;
419 new_stack->ref = &new_stack->next;
420 new_stack->full_url = full_url;
421 list_insert(new_stack, (&app_data.imported_stack_head));
423 scss_doc_set_user_data(parser->doc, &app_data);
425 handler->startSelector = s_css_parser_from_uri_start_selector;
426 handler->endSelector = s_css_parser_from_uri_end_selector;
427 handler->property = s_css_parser_from_uri_property;
428 handler->import = s_css_parser_from_uri_import_style;
430 scss_parse_stylesheet(parser);
431 DBG(r,"REQ[%X] url:[%s]", TO_ADDR(r),uri);
432 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
434 return s_merge_stylesheet(pool, old_stylesheet, app_data.stylesheet);
437 static css_stylesheet_t *
438 s_chxj_css_parse_from_buf(request_rec *r, apr_pool_t *pool, struct css_already_import_stack *imported_stack, css_stylesheet_t *old_stylesheet, const char *css)
441 SCSSParserPtr_t parser = NULL;
442 SCSSSACHandlerPtr_t handler = NULL;
443 css_stylesheet_t *stylesheet = NULL;
444 struct css_app_data app_data;
445 struct css_already_import_stack *new_stack;
447 DBG(r,"REQ[%X] start %s()",TO_ADDR(r),__func__);
448 DBG(r,"REQ[%X] css:[%s]", TO_ADDR(r),css);
449 srclen = strlen(css);
452 parser = scss_parser_new_from_buf(pool, css, "");
454 ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): scss_parser_new_from_buf() failed", TO_ADDR(r),APLOG_MARK);
457 handler = scss_doc_handler_new(parser);
459 ERR(r,"REQ[%X] %s:%d end chxj_css_parse_from_uri(): scss_doc_handler_new() failed", TO_ADDR(r),APLOG_MARK);
463 stylesheet = apr_palloc(pool, sizeof(*stylesheet));
464 memset(stylesheet, 0, sizeof(*stylesheet));
465 stylesheet->selector_head.next = &stylesheet->selector_head;
466 stylesheet->selector_head.ref = &stylesheet->selector_head.next;
468 memset(&app_data, 0, sizeof(struct css_app_data));
469 app_data.stylesheet = stylesheet;
470 app_data.selector_list = NULL;
471 app_data.selector_count = 0;
472 app_data.pool = pool;
473 app_data.error_occured = 0;
475 if (imported_stack) {
476 s_copy_already_import_stack(pool, &app_data.imported_stack_head, imported_stack);
479 app_data.imported_stack_head.next = &app_data.imported_stack_head;
480 app_data.imported_stack_head.ref = &app_data.imported_stack_head.next;
483 scss_doc_set_user_data(parser->doc, &app_data);
485 new_stack = apr_palloc(pool, sizeof(*new_stack));
486 memset(new_stack, 0, sizeof(*new_stack));
487 new_stack->next = new_stack;
488 new_stack->ref = &new_stack->next;
489 new_stack->full_url = "";
490 list_insert(new_stack, (&app_data.imported_stack_head));
492 handler->startSelector = s_css_parser_from_uri_start_selector;
493 handler->endSelector = s_css_parser_from_uri_end_selector;
494 handler->property = s_css_parser_from_uri_property;
495 handler->import = s_css_parser_from_uri_import_style;
497 scss_parse_stylesheet(parser);
498 DBG(r,"REQ[%X] css:[%s]", TO_ADDR(r),css);
499 DBG(r,"REQ[%X] end %s()",TO_ADDR(r),__func__);
500 return s_merge_stylesheet(pool, old_stylesheet, app_data.stylesheet);
504 #define ERROR_OCCORED do { \
505 if (app_data->error_occured) { \
513 struct css_app_data *app_data = (struct css_app_data *)scss_doc_get_user_data(parser->doc)
518 s_css_parser_from_uri_start_selector(SCSSParserPtr_t parser, SCSSNodePtr_t selectors)
521 SCSSNodePtr_t cur = NULL;
525 app_data->selector_count = 0;
526 for (cur = selectors->next; cur != selectors; cur = cur->next)
527 app_data->selector_count++;
529 app_data->selector_list = apr_palloc(app_data->pool, sizeof(char *) * app_data->selector_count);
530 if (! app_data->selector_list) {
531 ERR(app_data->r, "REQ[%X] %s:%d Out of memory", TO_ADDR(app_data->r),APLOG_MARK);
532 app_data->error_occured = 1;
536 for (cur = selectors->next; cur != selectors; cur = cur->next) {
537 app_data->selector_list[ii++] = apr_pstrdup(app_data->pool, (char *)cur->name);
539 app_data->property_head.next = &app_data->property_head;
540 app_data->property_head.ref = &app_data->property_head.next;
545 s_css_parser_from_uri_end_selector(SCSSParserPtr_t parser, SCSSNodePtr_t UNUSED(selectors))
548 css_property_t *cur = NULL;
552 if (app_data->property_head.next) {
553 for (ii=0; ii<app_data->selector_count; ii++) {
554 css_selector_t *sel = s_new_selector(app_data->pool, app_data->stylesheet, app_data->selector_list[ii]);
556 for (cur = app_data->property_head.next; cur && cur != &app_data->property_head; cur = cur->next) {
557 css_property_t *tgt = s_css_parser_copy_property(app_data->pool, cur);
558 s_merge_property(sel, tgt);
560 css_selector_t *point_selector = &app_data->stylesheet->selector_head;
561 list_insert(sel, point_selector);
564 app_data->property_head.next = &app_data->property_head;
565 app_data->property_head.ref = &app_data->property_head.next;
569 s_merge_property(css_selector_t *sel, css_property_t *tgt)
572 css_property_t *pnt = &sel->property_head;
573 char l = tolower(*tgt->name);
574 char u = toupper(*tgt->name);
575 for (cur = pnt->next; cur != pnt;cur = cur->next) {
576 if ((l == *cur->name || u == *cur->name) && strcasecmp(cur->name, tgt->name) == 0) {
577 cur->value = tgt->value;
581 list_insert(tgt, pnt);
584 static css_selector_t *
585 s_new_selector(apr_pool_t *pool, css_stylesheet_t *stylesheet, char *name)
587 css_selector_t *sel = NULL;
588 sel = s_search_selector(stylesheet, name);
592 sel->ref = &sel->next;
595 sel = apr_palloc(pool, sizeof(css_selector_t));
596 memset(sel, 0, sizeof(css_selector_t));
599 sel->ref = &sel->next;
600 sel->property_head.next = &sel->property_head;
601 sel->property_head.ref = &sel->property_head.next;
606 static css_selector_t *
607 s_search_selector(css_stylesheet_t *stylesheet, const char *name)
610 char l = tolower(*name);
611 char u = toupper(*name);
612 if (! stylesheet) return NULL;
613 for (cur = stylesheet->selector_head.next; cur != &stylesheet->selector_head; cur = cur->next) {
614 if (cur->name && (l == *cur->name || u == *cur->name) && strcasecmp(cur->name, name) == 0) {
621 static css_property_t *
622 s_css_parser_copy_property(apr_pool_t *pool, css_property_t *from)
624 css_property_t *prop = apr_palloc(pool, sizeof(css_property_t));
625 prop->name = apr_pstrdup(pool, from->name);
626 prop->value = apr_pstrdup(pool, from->value);
628 prop->ref = &prop->next;
636 s_css_parser_from_uri_property(SCSSParserPtr_t parser, const char *propertyName, const char *value, int UNUSED(impotant))
640 css_property_t *property;
642 if (propertyName && value) {
643 property = apr_palloc(app_data->pool, sizeof(*property));
644 memset(property, 0, sizeof(*property));
645 property->name = apr_pstrdup(app_data->pool, propertyName);
646 property->value = apr_pstrdup(app_data->pool, value);
648 css_property_t *point_property = &app_data->property_head;
649 list_insert(property, point_property);
657 s_css_parser_from_uri_import_style(SCSSParserPtr_t parser, const char *uri, const char **media, const char *UNUSED(defaultNamespaceURI))
663 css_stylesheet_t *new_stylesheet = NULL;
665 for (ii=0; ii<SCSS_MEDIA_TYPE_NUM; ii++) {
666 if (('h' == *media[ii] || 'H' == *media[ii]) && strcasecmp(media[ii], "handheld") == 0) {
670 if (('a' == *media[ii] || 'A' == *media[ii]) && strcasecmp(media[ii], "all") == 0) {
677 char *new_url = NULL;
678 char *base_url = NULL;
680 base_url = s_uri_to_base_url(&app_data->r->parsed_uri, app_data->pool);
681 new_url = s_path_to_fullurl(app_data->pool, base_url, app_data->r->parsed_uri.path, uri);
683 new_stylesheet = s_chxj_css_parse_from_uri(app_data->r, app_data->pool, &app_data->imported_stack_head, app_data->stylesheet, new_url);
684 if (new_stylesheet) {
685 app_data->stylesheet = new_stylesheet;
693 s_path_to_fullurl(apr_pool_t *pool, const char *base_url, const char *base_path, const char *uri)
695 char *new_url = NULL;
696 if (chxj_starts_with(uri, "http")) {
697 return apr_pstrdup(pool, uri);
701 return apr_pstrcat(pool, base_url, uri, NULL);
704 new_url = apr_pstrcat(pool, base_url, base_path, NULL);
705 if (new_url[strlen(new_url)-1] == '/') {
706 new_url = apr_pstrcat(pool, new_url, uri, NULL);
709 new_url = apr_pstrcat(pool, new_url, "/", uri, NULL);
716 s_uri_to_base_url(apr_uri_t *uri, apr_pool_t *pool)
718 char *new_url = apr_psprintf(pool, "%s://%s", uri->scheme, uri->hostname);
719 if (strcmp(uri->scheme, "http") == 0) {
720 if (uri->port != 80 && uri->port != 0) {
721 new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
724 else if (strcmp(uri->scheme, "https") == 0) {
725 if (uri->port != 443 && uri->port != 0) {
726 new_url = apr_pstrcat(pool, new_url, apr_psprintf(pool, ":%d", uri->port), NULL);
733 s_is_already_imported(struct css_already_import_stack *imported_stack_head, const char *url)
735 struct css_already_import_stack *cur;
736 char l = tolower(*url);
737 char u = toupper(*url);
738 for (cur = imported_stack_head->next; cur != imported_stack_head; cur = cur->next) {
739 if ((l == *cur->full_url || u == *cur->full_url) && strcasecmp(url, cur->full_url) == 0) {
746 static css_stylesheet_t *
747 s_merge_stylesheet(apr_pool_t *pool, css_stylesheet_t *old_stylesheet, css_stylesheet_t *new_stylesheet)
750 if (! old_stylesheet) {
751 return new_stylesheet;
754 for (cur = new_stylesheet->selector_head.next; cur != &new_stylesheet->selector_head; cur = cur->next) {
755 char *name = cur->name;
756 char l = tolower(*name);
757 char u = toupper(*name);
758 css_selector_t *cur_old;
762 for (cur_old = old_stylesheet->selector_head.next; cur_old != &old_stylesheet->selector_head; cur_old = cur_old->next) {
763 char *oldname = cur_old->name;
764 if ((l == *oldname || u == *oldname) && strcasecmp(name, oldname) == 0) {
765 css_property_t *cur_prop;
766 for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
767 css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
768 s_merge_property(cur_old, target);
775 /* add new selector */
776 css_property_t *cur_prop;
777 css_selector_t *new_selector = apr_palloc(pool, sizeof(*new_selector));
778 memset(new_selector, 0, sizeof(*new_selector));
779 new_selector->next = new_selector;
780 new_selector->ref = &new_selector->next;
781 new_selector->property_head.next = &new_selector->property_head;
782 new_selector->property_head.ref = &new_selector->property_head.next;
783 new_selector->name = apr_pstrdup(pool, name);
784 for (cur_prop = cur->property_head.next; cur_prop != &cur->property_head; cur_prop = cur_prop->next) {
785 css_property_t *target = s_css_parser_copy_property(pool, cur_prop);
786 list_insert(target, (&new_selector->property_head));
788 list_insert(new_selector, (&old_stylesheet->selector_head));
792 return old_stylesheet;
797 s_copy_already_import_stack(apr_pool_t *pool, struct css_already_import_stack *base, struct css_already_import_stack *imported_stack)
799 struct css_already_import_stack *cur;
802 base->ref = &base->next;
804 for (cur = imported_stack->next; cur != imported_stack; cur = cur->next) {
805 struct css_already_import_stack *new_stack;
806 new_stack = apr_palloc(pool, sizeof(*new_stack));
807 memset(new_stack, 0, sizeof(*new_stack));
808 new_stack->full_url = apr_pstrdup(pool, cur->full_url);
809 list_insert(new_stack, base);
815 chxj_css_stylesheet_dump(css_stylesheet_t *stylesheet)
817 css_selector_t *cur_sel;
818 css_property_t *cur_prop;
820 for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
821 fprintf(stderr, "selector:[%s]\n", cur_sel->name);
822 for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
823 fprintf(stderr, "\tproperty:\n");
824 fprintf(stderr, "\t\t- name:%s\n", cur_prop->name);
825 fprintf(stderr, "\t\t- value:%s\n", cur_prop->value);
831 s_get_tag_and_class_and_id(Doc *doc, Node *node, char **tag_name, char **class_name, char **id)
834 *tag_name = node->name;
835 for (attr = qs_get_attr(doc, node); attr; attr = qs_get_next_attr(doc,attr)) {
836 char *name = qs_get_attr_name(doc,attr);
837 char *value = qs_get_attr_value(doc,attr);
838 if (STRCASEEQ('c','C', "class", name)) {
844 if (STRCASEEQ('i','I', "id", name)) {
849 if (*id && *class_name) break;
854 s_cmp_now_node_vs_current_style(Doc *doc, request_rec *r, apr_pool_t *pool, char *src, ap_regex_t *pattern4, Node *node)
856 ap_regmatch_t match[256];
857 if (chxj_ap_regexec(pattern4, src, pattern4->re_nsub + 1, match, 0) == 0) {
858 char *tag_name = chxj_ap_pregsub(pool, "$1", src, pattern4->re_nsub + 1, match);
859 char *class_name = chxj_ap_pregsub(pool, "$2", src, pattern4->re_nsub + 1, match);
860 char *id_name = chxj_ap_pregsub(pool, "$3", src, pattern4->re_nsub + 1, match);
861 DBG(r, "REQ[%X] tag:[%s] class:[%s] id:[%s]", TO_ADDR(r),tag_name, class_name, id_name);
863 DBG(r, "REQ[%X] unmatch(parent is null)",TO_ADDR(r));
866 char *node_tag_name = NULL;
867 char *node_class_name = NULL;
868 char *node_id_name = NULL;
869 s_get_tag_and_class_and_id(doc, node, &node_tag_name, &node_class_name, &node_id_name);
870 if (*tag_name == 0 || strcasecmp(node_tag_name, tag_name) == 0 || strcmp("*", tag_name) == 0) {
871 if (class_name && *class_name != 0) {
872 if (!node_class_name) {
873 DBG(r, "REQ[%X] unmatch (class) node:[NULL]",TO_ADDR(r));
876 if (strcasecmp(node_class_name, &class_name[1]) != 0) {
877 DBG(r,"REQ[%X] unmatch (class) node:[%s] style:[%s]", TO_ADDR(r),node_class_name, &class_name[1]);
881 if (id_name && *id_name != 0) {
883 DBG(r,"REQ[%X] unmatch (id) node:[NULL]",TO_ADDR(r));
886 if (strcasecmp(node_id_name, &id_name[1]) != 0) {
887 DBG(r,"REQ[%X] unmatch (id)",TO_ADDR(r));
891 DBG(r,"REQ[%X] match",TO_ADDR(r));
894 DBG(r,"REQ[%X] unmatch(tag) tag:[%s] vs [%s]", TO_ADDR(r),tag_name, node_tag_name);
901 * find selector with pseudo class.
902 * @return new css_stylesheet_t
905 chxj_find_pseudo_selectors(Doc *doc, css_stylesheet_t *stylesheet)
907 css_selector_t *cur_sel;
908 css_stylesheet_t *result;
909 char *pattern_str = "^a:(link|focus|visited)$";
910 ap_regex_t *pattern1 = chxj_ap_pregcomp(doc->pool, pattern_str, AP_REG_EXTENDED|AP_REG_ICASE);
912 result = apr_palloc(doc->pool, sizeof(*result));
914 ERR(doc->r,"REQ[%X] %s:%d Out of Memory", TO_ADDR(doc->r),APLOG_MARK);
917 memset(result, 0, sizeof(*result));
918 result->selector_head.next = &result->selector_head;
919 result->selector_head.ref = &result->selector_head.next;
921 for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
922 ap_regmatch_t match[256];
923 if (chxj_ap_regexec(pattern1, cur_sel->name, pattern1->re_nsub + 1, match, 0) == 0) {
924 css_selector_t *new_sel = s_new_selector(doc->pool, result, cur_sel->name);
925 css_property_t *cur_prop;
926 for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
927 css_property_t *to_prop = s_css_parser_copy_property(doc->pool, cur_prop);
928 list_insert(to_prop, (&new_sel->property_head));
930 list_insert(new_sel, (&result->selector_head));
934 chxj_ap_pregfree(doc->pool, pattern1);
940 * style attribute parser.
941 * @return merged new css_stylesheet_t object.
944 chxj_css_parse_style_attr(Doc *doc, css_stylesheet_t *old_stylesheet, char *tag_name, char *class_name, char *id_name, char *style_attr_value)
946 css_stylesheet_t *new_stylesheet;
947 css_stylesheet_t *dup_stylesheet = NULL;
949 char *class_name_sel = NULL;
950 char *id_name_sel = NULL;
952 DBG(doc->r,"REQ[%X] start %s()",TO_ADDR(doc->r),__func__);
955 class_name_sel = apr_psprintf(doc->pool, ".%s", class_name);
958 id_name_sel = apr_psprintf(doc->pool, "#%s", id_name);
961 attr_value = apr_psprintf(doc->pool,
964 (class_name) ? class_name_sel : "",
965 (id_name) ? id_name_sel : "",
968 if (old_stylesheet) {
969 dup_stylesheet = s_dup_stylesheet(doc, old_stylesheet);
971 new_stylesheet = s_chxj_css_parse_from_buf(doc->r, doc->pool, NULL, dup_stylesheet, attr_value);
973 DBG(doc->r,"REQ[%X] end %s()",TO_ADDR(doc->r),__func__);
974 return new_stylesheet;
978 static css_stylesheet_t *
979 s_dup_stylesheet(Doc *doc, css_stylesheet_t *stylesheet)
981 css_selector_t *cur_sel;
982 css_stylesheet_t *result;
984 result = apr_palloc(doc->pool, sizeof(*result));
986 ERR(doc->r, "REQ[%X] %s:%d Out of Memory", TO_ADDR(doc->r),APLOG_MARK);
989 memset(result, 0, sizeof(*result));
990 result->selector_head.next = &result->selector_head;
991 result->selector_head.ref = &result->selector_head.next;
993 for (cur_sel = stylesheet->selector_head.next; cur_sel != &stylesheet->selector_head; cur_sel = cur_sel->next) {
994 css_selector_t *new_sel = s_new_selector(doc->pool, result, cur_sel->name);
995 css_property_t *cur_prop;
996 for (cur_prop = cur_sel->property_head.next; cur_prop != &cur_sel->property_head; cur_prop = cur_prop->next) {
997 css_property_t *to_prop = s_css_parser_copy_property(doc->pool, cur_prop);
998 list_insert(to_prop, (&new_sel->property_head));
1000 list_insert(new_sel, (&result->selector_head));
1007 * style value parser.
1008 * <style>css</style>
1009 * @return merged new css_stylesheet_t object.
1012 chxj_css_parse_style_value(Doc *doc, css_stylesheet_t *old_stylesheet, char *style_attr_value)
1014 css_stylesheet_t *new_stylesheet;
1015 css_stylesheet_t *dup_stylesheet = NULL;
1017 DBG(doc->r,"REQ[%X] start %s()",TO_ADDR(doc->r),__func__);
1019 if (old_stylesheet) {
1020 dup_stylesheet = s_dup_stylesheet(doc, old_stylesheet);
1022 new_stylesheet = s_chxj_css_parse_from_buf(doc->r, doc->pool, NULL, dup_stylesheet, style_attr_value);
1024 DBG(doc->r,"REQ[%X] end %s()",TO_ADDR(doc->r),__func__);
1025 return new_stylesheet;
1030 * allocate prop_list_stack.
1032 css_prop_list_stack_t *
1033 chxj_new_prop_list_stack(Doc *doc)
1035 css_prop_list_stack_t *new_stack = apr_palloc(doc->pool, sizeof(css_prop_list_stack_t));
1037 ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1040 new_stack->head.next = &new_stack->head;
1041 new_stack->head.ref = &new_stack->head.next;
1046 chxj_css_create_prop_list(Doc *doc, css_selector_t *sel)
1048 css_prop_list_t *prop_list;
1049 prop_list = apr_palloc(doc->pool, sizeof(*prop_list));
1051 ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1054 prop_list->head.next = &prop_list->head;
1055 prop_list->head.ref = &prop_list->head.next;
1057 css_property_t *cur_prop;
1058 for (cur_prop = sel->property_head.next;
1059 cur_prop != &sel->property_head;
1060 cur_prop = cur_prop->next) {
1061 css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1062 list_insert(cp_prop, (&prop_list->head));
1072 chxj_css_push_prop_list(css_prop_list_stack_t *stack, css_prop_list_t *prop_list)
1074 list_insert(prop_list, (&stack->head));
1081 chxj_css_pop_prop_list(css_prop_list_stack_t *stack)
1083 css_prop_list_t *tail = chxj_css_get_last_prop_list(stack);
1097 chxj_css_get_last_prop_list(css_prop_list_stack_t *stack)
1099 css_prop_list_t *tail = (css_prop_list_t *)((apr_size_t)stack->head.ref - (apr_size_t)APR_OFFSETOF(css_prop_list_t, next));
1100 if (tail == &stack->head) {
1107 chxj_dup_css_prop_list(Doc *doc, css_prop_list_t *old)
1109 css_property_t *cur_prop;
1110 css_prop_list_t *new_prop_list;
1112 new_prop_list = chxj_css_create_prop_list(doc, NULL);
1113 if (! new_prop_list) {
1114 ERR(doc->r,"REQ[%X] %s:%d Out of memory.",TO_ADDR(doc->r),APLOG_MARK);
1118 for (cur_prop = old->head.next; cur_prop != &old->head; cur_prop = cur_prop->next) {
1119 css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1120 list_insert(cp_prop, (&new_prop_list->head));
1123 return new_prop_list;
1126 chxj_css_prop_list_merge_property(Doc *doc, css_prop_list_t *base, css_selector_t *sel)
1128 css_property_t *cur_prop;
1129 css_property_t *b_prop;
1130 for (cur_prop = sel->property_head.next; cur_prop != &sel->property_head; cur_prop = cur_prop->next) {
1132 for (b_prop = base->head.next; b_prop != &base->head; b_prop = b_prop->next) {
1133 if (cur_prop->name && b_prop->name && strcasecmp(cur_prop->name, b_prop->name) == 0) {
1135 b_prop->value = apr_pstrdup(doc->pool, cur_prop->value);
1139 css_property_t *cp_prop = s_css_parser_copy_property(doc->pool, cur_prop);
1140 list_insert(cp_prop, (&base->head));
1147 chxj_css_get_property_value(Doc *doc, css_prop_list_t *base, const char *name)
1149 css_property_t *b_prop;
1150 css_property_t *result;
1152 result = apr_palloc(doc->pool, sizeof(*result));
1153 memset(result, 0, sizeof(*result));
1154 result->next = result;
1155 result->ref = &result->next;
1157 for (b_prop = base->head.next; b_prop != &base->head; b_prop = b_prop->next) {
1158 if (b_prop->name && strcasecmp(name, b_prop->name) == 0) {
1159 css_property_t *tmp;
1161 tmp = apr_palloc(doc->pool, sizeof(*tmp));
1162 memset(tmp, 0, sizeof(*tmp));
1164 tmp->ref = &tmp->next;
1166 tmp->value = apr_pstrdup(doc->pool, b_prop->value);
1167 list_insert(tmp, result);
1175 chxj_css_rgb_func_to_value(apr_pool_t *pool, const char *rgb_func_string)
1177 char *s = apr_pstrdup(pool, rgb_func_string);
1178 if (chxj_starts_with(s, "rgb")) {
1180 s = qs_trim_string(pool, s);
1182 int len = strlen(s);
1183 if (s[len - 1] == ')') s[len-1] = 0;
1185 char *red = qs_trim_string(pool, apr_strtok(s, ",", &pstat));
1186 char *green = qs_trim_string(pool, apr_strtok(NULL, ",", &pstat));
1187 char *blue = qs_trim_string(pool, apr_strtok(NULL, ",", &pstat));
1188 int red_per_flag = 0;
1189 int green_per_flag = 0;
1190 int blue_per_flag = 0;
1191 if (red && (pstat = strchr(red, '%'))) {
1198 if (green && (pstat = strchr(green, '%'))) {
1205 if (blue && (pstat = strchr(blue, '%'))) {
1212 double d_red = (double)chxj_atoi(red);
1213 double d_green = (double)chxj_atoi(green);
1214 double d_blue = (double)chxj_atoi(blue);
1219 if (green_per_flag) {
1223 if (blue_per_flag) {
1227 char *ret = apr_psprintf(pool, "#%02x%02x%02x", (int)d_red, (int)d_green, (int)d_blue);
1230 else if (s[0] == '#') {
1231 if (strlen(&s[1]) == 3) {
1241 int ired = chxj_axtoi(red);
1242 int igreen = chxj_axtoi(green);
1243 int iblue = chxj_axtoi(blue);
1244 return apr_psprintf(pool, "#%02x%02x%02x", ired * 0x10 + ired, igreen * 0x10 + igreen, iblue * 0x10 + iblue);
1251 if (STRCASEEQ('q','Q', &s[1], "qua")) {
1253 return apr_pstrdup(pool, "#00ffff");
1258 if (s[1] == 'l' || s[1] == 'L') {
1259 if (s[2] == 'a' || s[2] == 'A') {
1260 if (STRCASEEQ('c','C',&s[3], "ck")) {
1262 return apr_pstrdup(pool, "#000000");
1265 else if (s[2] == 'u' || s[2] == 'U') {
1266 if (s[3] == 'e' || s[3] == 'E') {
1268 return apr_pstrdup(pool, "#0000ff");
1275 if (STRCASEEQ('u','U',&s[1], "uchsia")) {
1277 return apr_pstrdup(pool, "#ff00ff");
1282 if (s[1] == 'r'||s[1] == 'R') {
1283 if (STRCASEEQ('a','A', &s[2], "ay")) {
1285 return apr_pstrdup(pool, "#808080");
1287 else if (STRCASEEQ('e','E',&s[2], "een")) {
1289 return apr_pstrdup(pool, "#008000");
1295 if (STRCASEEQ('i','I', &s[1], "ime")) {
1297 return apr_pstrdup(pool, "#00ff00");
1302 if (STRCASEEQ('a','A', &s[1], "aroon")) {
1304 return apr_pstrdup(pool, "#800000");
1309 if (STRCASEEQ('a','A', &s[1], "avy")) {
1311 return apr_pstrdup(pool, "#000080");
1316 if (STRCASEEQ('l','L', &s[1], "live")) {
1318 return apr_pstrdup(pool, "#808000");
1323 if (STRCASEEQ('u','U', &s[1], "urple")) {
1325 return apr_pstrdup(pool, "#800080");
1330 if (STRCASEEQ('e','E', &s[1], "ed")) {
1332 return apr_pstrdup(pool, "#ff0000");
1337 if (STRCASEEQ('i','I', &s[1], "ilver")) {
1339 return apr_pstrdup(pool, "#c0c0c0");
1344 if (STRCASEEQ('e','E', &s[1], "eal")) {
1346 return apr_pstrdup(pool, "#008080");
1351 if (STRCASEEQ('h','H', &s[1], "hite")) {
1353 return apr_pstrdup(pool, "#ffffff");
1358 if (STRCASEEQ('y','Y', &s[1], "ellow")) {
1360 return apr_pstrdup(pool, "#ffff00");