PageRenderTime 64ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/cssed-0.4.0/libcroco/parser/cr-sel-eng.c

#
C | 1555 lines | 1044 code | 180 blank | 331 comment | 329 complexity | 952cb9f104507a46704af74444047d28 MD5 | raw file
Possible License(s): GPL-2.0
  1. /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
  2. /*
  3. * This file is part of The Croco Library
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of version 2.1 of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser
  15. * General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  18. * USA
  19. *
  20. * See COPYRIGHTS file for copyright informations.
  21. */
  22. #include <string.h>
  23. #include "cr-sel-eng.h"
  24. /**
  25. *@file:
  26. *The definition of the #CRSelEng class.
  27. *The #CRSelEng is actually the "Selection Engine"
  28. *class. This is highly experimental for at the moment and
  29. *its api is very likely to change in a near future.
  30. */
  31. #define PRIVATE(a_this) (a_this)->priv
  32. struct CRPseudoClassSelHandlerEntry {
  33. guchar *name;
  34. enum CRPseudoType type;
  35. CRPseudoClassSelectorHandler handler;
  36. };
  37. struct _CRSelEngPriv {
  38. /*not used yet */
  39. gboolean case_sensitive;
  40. CRStyleSheet *sheet;
  41. /**
  42. *where to store the next statement
  43. *to be visited so that we can remember
  44. *it from one method call to another.
  45. */
  46. CRStatement *cur_stmt;
  47. GList *pcs_handlers;
  48. gint pcs_handlers_size;
  49. } ;
  50. static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
  51. xmlNode * a_node);
  52. static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
  53. xmlNode * a_node);
  54. static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
  55. xmlNode * a_node);
  56. static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
  57. CRSimpleSel * a_sel,
  58. xmlNode * a_node,
  59. gboolean * a_result,
  60. gboolean a_eval_sel_list_from_end,
  61. gboolean a_recurse);
  62. static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
  63. CRStyleSheet *
  64. a_stylesheet,
  65. xmlNode * a_node,
  66. CRStatement **
  67. a_rulesets,
  68. gulong * a_len);
  69. static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
  70. CRStatement *
  71. a_ruleset);
  72. static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
  73. CRAdditionalSel *
  74. a_add_sel,
  75. xmlNode * a_node);
  76. static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
  77. CRAdditionalSel * a_sel,
  78. xmlNode * a_node);
  79. static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
  80. CRAdditionalSel * a_sel,
  81. xmlNode * a_node);
  82. static xmlNode *get_next_element_node (xmlNode * a_node);
  83. static xmlNode *get_next_child_element_node (xmlNode * a_node);
  84. static xmlNode *get_prev_element_node (xmlNode * a_node);
  85. static xmlNode *get_next_parent_element_node (xmlNode * a_node);
  86. static gboolean
  87. lang_pseudo_class_handler (CRSelEng * a_this,
  88. CRAdditionalSel * a_sel, xmlNode * a_node)
  89. {
  90. xmlNode *node = a_node;
  91. xmlChar *val = NULL;
  92. gboolean result = FALSE;
  93. g_return_val_if_fail (a_this && PRIVATE (a_this)
  94. && a_sel && a_sel->content.pseudo
  95. && a_sel->content.pseudo
  96. && a_sel->content.pseudo->name
  97. && a_sel->content.pseudo->name->stryng
  98. && a_node, CR_BAD_PARAM_ERROR);
  99. if (strncmp (a_sel->content.pseudo->name->stryng->str,
  100. "lang", 4)
  101. || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
  102. cr_utils_trace_info ("This handler is for :lang only");
  103. return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
  104. }
  105. /*lang code should exist and be at least of length 2 */
  106. if (!a_sel->content.pseudo->extra
  107. || !a_sel->content.pseudo->extra->stryng
  108. || a_sel->content.pseudo->extra->stryng->len < 2)
  109. return FALSE;
  110. for (; node; node = get_next_parent_element_node (node)) {
  111. val = xmlGetProp (node, "lang");
  112. if (val
  113. && !strncmp (val,
  114. a_sel->content.pseudo->extra->stryng->str,
  115. a_sel->content.pseudo->extra->stryng->len)) {
  116. result = TRUE;
  117. }
  118. if (val) {
  119. xmlFree (val);
  120. val = NULL;
  121. }
  122. }
  123. return result;
  124. }
  125. static gboolean
  126. first_child_pseudo_class_handler (CRSelEng * a_this,
  127. CRAdditionalSel * a_sel, xmlNode * a_node)
  128. {
  129. xmlNode *node = NULL;
  130. g_return_val_if_fail (a_this && PRIVATE (a_this)
  131. && a_sel && a_sel->content.pseudo
  132. && a_sel->content.pseudo
  133. && a_sel->content.pseudo->name
  134. && a_sel->content.pseudo->name->stryng
  135. && a_node, CR_BAD_PARAM_ERROR);
  136. if (strcmp (a_sel->content.pseudo->name->stryng->str,
  137. "first-child")
  138. || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
  139. cr_utils_trace_info ("This handler is for :first-child only");
  140. return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
  141. }
  142. if (!a_node->parent)
  143. return FALSE;
  144. node = get_next_child_element_node (a_node->parent);
  145. if (node == a_node)
  146. return TRUE;
  147. return FALSE;
  148. }
  149. static gboolean
  150. pseudo_class_add_sel_matches_node (CRSelEng * a_this,
  151. CRAdditionalSel * a_add_sel,
  152. xmlNode * a_node)
  153. {
  154. enum CRStatus status = CR_OK;
  155. CRPseudoClassSelectorHandler handler = NULL;
  156. g_return_val_if_fail (a_this && PRIVATE (a_this)
  157. && a_add_sel
  158. && a_add_sel->content.pseudo
  159. && a_add_sel->content.pseudo->name
  160. && a_add_sel->content.pseudo->name->stryng
  161. && a_add_sel->content.pseudo->name->stryng->str
  162. && a_node, CR_BAD_PARAM_ERROR);
  163. status = cr_sel_eng_get_pseudo_class_selector_handler
  164. (a_this, a_add_sel->content.pseudo->name->stryng->str,
  165. a_add_sel->content.pseudo->type, &handler);
  166. if (status != CR_OK || !handler)
  167. return FALSE;
  168. return handler (a_this, a_add_sel, a_node);
  169. }
  170. /**
  171. *@param a_add_sel the class additional selector to consider.
  172. *@param a_node the xml node to consider.
  173. *@return TRUE if the class additional selector matches
  174. *the xml node given in argument, FALSE otherwise.
  175. */
  176. static gboolean
  177. class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
  178. {
  179. gboolean result = FALSE;
  180. xmlChar *klass = NULL,
  181. *cur = NULL;
  182. g_return_val_if_fail (a_add_sel
  183. && a_add_sel->type == CLASS_ADD_SELECTOR
  184. && a_add_sel->content.class_name
  185. && a_add_sel->content.class_name->stryng
  186. && a_add_sel->content.class_name->stryng->str
  187. && a_node, FALSE);
  188. if (xmlHasProp (a_node, "class")) {
  189. klass = xmlGetProp (a_node, "class");
  190. for (cur = klass; cur && *cur; cur++) {
  191. while (cur && *cur
  192. && cr_utils_is_white_space (*cur)
  193. == TRUE)
  194. cur++;
  195. if (!strncmp (cur,
  196. a_add_sel->content.class_name->stryng->str,
  197. a_add_sel->content.class_name->stryng->len)) {
  198. cur += a_add_sel->content.class_name->stryng->len;
  199. if ((cur && !*cur)
  200. || cr_utils_is_white_space (*cur) == TRUE)
  201. result = TRUE;
  202. }
  203. if (cur && !*cur)
  204. break ;
  205. }
  206. }
  207. if (klass) {
  208. xmlFree (klass);
  209. klass = NULL;
  210. }
  211. return result;
  212. }
  213. /**
  214. *@return TRUE if the additional attribute selector matches
  215. *the current xml node given in argument, FALSE otherwise.
  216. *@param a_add_sel the additional attribute selector to consider.
  217. *@param a_node the xml node to consider.
  218. */
  219. static gboolean
  220. id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
  221. {
  222. gboolean result = FALSE;
  223. xmlChar *id = NULL;
  224. g_return_val_if_fail (a_add_sel
  225. && a_add_sel->type == ID_ADD_SELECTOR
  226. && a_add_sel->content.id_name
  227. && a_add_sel->content.id_name->stryng
  228. && a_add_sel->content.id_name->stryng->str
  229. && a_node, FALSE);
  230. g_return_val_if_fail (a_add_sel
  231. && a_add_sel->type == ID_ADD_SELECTOR
  232. && a_node, FALSE);
  233. if (xmlHasProp (a_node, "id")) {
  234. id = xmlGetProp (a_node, "id");
  235. if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
  236. a_add_sel->content.id_name->stryng->len)) {
  237. result = TRUE;
  238. }
  239. }
  240. if (id) {
  241. xmlFree (id);
  242. id = NULL;
  243. }
  244. return result;
  245. }
  246. /**
  247. *Returns TRUE if the instance of #CRAdditional selector matches
  248. *the node given in parameter, FALSE otherwise.
  249. *@param a_add_sel the additional selector to evaluate.
  250. *@param a_node the xml node against whitch the selector is to
  251. *be evaluated
  252. *return TRUE if the additional selector matches the current xml node
  253. *FALSE otherwise.
  254. */
  255. static gboolean
  256. attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
  257. {
  258. CRAttrSel *cur_sel = NULL;
  259. g_return_val_if_fail (a_add_sel
  260. && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
  261. && a_node, FALSE);
  262. for (cur_sel = a_add_sel->content.attr_sel;
  263. cur_sel; cur_sel = cur_sel->next) {
  264. switch (cur_sel->match_way) {
  265. case SET:
  266. if (!cur_sel->name
  267. || !cur_sel->name->stryng
  268. || !cur_sel->name->stryng->str)
  269. return FALSE;
  270. if (!xmlHasProp (a_node,
  271. cur_sel->name->stryng->str))
  272. return FALSE;
  273. break;
  274. case EQUALS:
  275. {
  276. xmlChar *value = NULL;
  277. if (!cur_sel->name
  278. || !cur_sel->name->stryng
  279. || !cur_sel->name->stryng->str
  280. || !cur_sel->value
  281. || !cur_sel->value->stryng
  282. || !cur_sel->value->stryng->str)
  283. return FALSE;
  284. if (!xmlHasProp
  285. (a_node,
  286. cur_sel->name->stryng->str))
  287. return FALSE;
  288. value = xmlGetProp
  289. (a_node,
  290. cur_sel->name->stryng->str);
  291. if (value
  292. && strcmp
  293. (value,
  294. cur_sel->value->stryng->str)) {
  295. xmlFree (value);
  296. return FALSE;
  297. }
  298. xmlFree (value);
  299. }
  300. break;
  301. case INCLUDES:
  302. {
  303. xmlChar *value = NULL,
  304. *ptr1 = NULL,
  305. *ptr2 = NULL,
  306. *cur = NULL;
  307. gboolean found = FALSE;
  308. if (!xmlHasProp
  309. (a_node,
  310. cur_sel->name->stryng->str))
  311. return FALSE;
  312. value = xmlGetProp
  313. (a_node,
  314. cur_sel->name->stryng->str);
  315. if (!value)
  316. return FALSE;
  317. /*
  318. *here, make sure value is a space
  319. *separated list of "words", where one
  320. *value is exactly cur_sel->value->str
  321. */
  322. for (cur = value; *cur; cur++) {
  323. /*
  324. *set ptr1 to the first non white space
  325. *char addr.
  326. */
  327. while (cr_utils_is_white_space
  328. (*cur) == TRUE && *cur)
  329. cur++;
  330. if (!*cur)
  331. break;
  332. ptr1 = cur;
  333. /*
  334. *set ptr2 to the end the word.
  335. */
  336. while (cr_utils_is_white_space
  337. (*cur) == FALSE && *cur)
  338. cur++;
  339. cur--;
  340. ptr2 = cur;
  341. if (!strncmp
  342. (ptr1,
  343. cur_sel->value->stryng->str,
  344. ptr2 - ptr1 + 1)) {
  345. found = TRUE;
  346. break;
  347. }
  348. ptr1 = ptr2 = NULL;
  349. }
  350. if (found == FALSE) {
  351. xmlFree (value);
  352. return FALSE;
  353. }
  354. xmlFree (value);
  355. }
  356. break;
  357. case DASHMATCH:
  358. {
  359. xmlChar *value = NULL,
  360. *ptr1 = NULL,
  361. *ptr2 = NULL,
  362. *cur = NULL;
  363. gboolean found = FALSE;
  364. if (!xmlHasProp
  365. (a_node,
  366. cur_sel->name->stryng->str))
  367. return FALSE;
  368. value = xmlGetProp
  369. (a_node,
  370. cur_sel->name->stryng->str);
  371. /*
  372. *here, make sure value is an hyphen
  373. *separated list of "words", each of which
  374. *starting with "cur_sel->value->str"
  375. */
  376. for (cur = value; *cur; cur++) {
  377. if (*cur == '-')
  378. cur++;
  379. ptr1 = cur;
  380. while (*cur != '-' && *cur)
  381. cur++;
  382. cur--;
  383. ptr2 = cur;
  384. if (g_strstr_len
  385. (ptr1, ptr2 - ptr1 + 1,
  386. cur_sel->value->stryng->str)
  387. == (gchar *) ptr1) {
  388. found = TRUE;
  389. break;
  390. }
  391. }
  392. if (found == FALSE) {
  393. xmlFree (value);
  394. return FALSE;
  395. }
  396. xmlFree (value);
  397. }
  398. break;
  399. default:
  400. return FALSE;
  401. }
  402. }
  403. return TRUE;
  404. }
  405. /**
  406. *Evaluates if a given additional selector matches an xml node.
  407. *@param a_add_sel the additional selector to consider.
  408. *@param a_node the xml node to consider.
  409. *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
  410. */
  411. static gboolean
  412. additional_selector_matches_node (CRSelEng * a_this,
  413. CRAdditionalSel * a_add_sel,
  414. xmlNode * a_node)
  415. {
  416. CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
  417. gboolean evaluated = FALSE ;
  418. for (tail = a_add_sel ;
  419. tail && tail->next;
  420. tail = tail->next) ;
  421. g_return_val_if_fail (tail, FALSE) ;
  422. for (cur_add_sel = tail ;
  423. cur_add_sel ;
  424. cur_add_sel = cur_add_sel->prev) {
  425. evaluated = TRUE ;
  426. if (cur_add_sel->type == NO_ADD_SELECTOR) {
  427. return FALSE;
  428. }
  429. if (cur_add_sel->type == CLASS_ADD_SELECTOR
  430. && cur_add_sel->content.class_name
  431. && cur_add_sel->content.class_name->stryng
  432. && cur_add_sel->content.class_name->stryng->str) {
  433. if (class_add_sel_matches_node (cur_add_sel,
  434. a_node) == FALSE) {
  435. return FALSE;
  436. }
  437. continue ;
  438. } else if (cur_add_sel->type == ID_ADD_SELECTOR
  439. && cur_add_sel->content.id_name
  440. && cur_add_sel->content.id_name->stryng
  441. && cur_add_sel->content.id_name->stryng->str) {
  442. if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
  443. return FALSE;
  444. }
  445. continue ;
  446. } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
  447. && cur_add_sel->content.attr_sel) {
  448. /*
  449. *here, call a function that does the match
  450. *against an attribute additionnal selector
  451. *and an xml node.
  452. */
  453. if (attr_add_sel_matches_node (cur_add_sel, a_node)
  454. == FALSE) {
  455. return FALSE;
  456. }
  457. continue ;
  458. } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
  459. && cur_add_sel->content.pseudo) {
  460. if (pseudo_class_add_sel_matches_node
  461. (a_this, cur_add_sel, a_node) == TRUE) {
  462. return TRUE;
  463. }
  464. return FALSE;
  465. }
  466. }
  467. if (evaluated == TRUE)
  468. return TRUE;
  469. return FALSE ;
  470. }
  471. static xmlNode *
  472. get_next_element_node (xmlNode * a_node)
  473. {
  474. xmlNode *cur_node = NULL;
  475. g_return_val_if_fail (a_node, NULL);
  476. cur_node = a_node->next;
  477. while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
  478. cur_node = cur_node->next;
  479. }
  480. return cur_node;
  481. }
  482. static xmlNode *
  483. get_next_child_element_node (xmlNode * a_node)
  484. {
  485. xmlNode *cur_node = NULL;
  486. g_return_val_if_fail (a_node, NULL);
  487. cur_node = a_node->children;
  488. if (!cur_node)
  489. return cur_node;
  490. if (a_node->children->type == XML_ELEMENT_NODE)
  491. return a_node->children;
  492. return get_next_element_node (a_node->children);
  493. }
  494. static xmlNode *
  495. get_prev_element_node (xmlNode * a_node)
  496. {
  497. xmlNode *cur_node = NULL;
  498. g_return_val_if_fail (a_node, NULL);
  499. cur_node = a_node->prev;
  500. while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
  501. cur_node = cur_node->prev;
  502. }
  503. return cur_node;
  504. }
  505. static xmlNode *
  506. get_next_parent_element_node (xmlNode * a_node)
  507. {
  508. xmlNode *cur_node = NULL;
  509. g_return_val_if_fail (a_node, NULL);
  510. cur_node = a_node->parent;
  511. while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
  512. cur_node = cur_node->parent;
  513. }
  514. return cur_node;
  515. }
  516. /**
  517. *Evaluate a selector (a simple selectors list) and says
  518. *if it matches the xml node given in parameter.
  519. *The algorithm used here is the following:
  520. *Walk the combinator separated list of simple selectors backward, starting
  521. *from the end of the list. For each simple selector, looks if
  522. *if matches the current node.
  523. *
  524. *@param a_this the selection engine.
  525. *@param a_sel the simple selection list.
  526. *@param a_node the xml node.
  527. *@param a_result out parameter. Set to true if the
  528. *selector matches the xml node, FALSE otherwise.
  529. *@param a_recurse if set to TRUE, the function will walk to
  530. *the next simple selector (after the evaluation of the current one)
  531. *and recursively evaluate it. Must be usually set to TRUE unless you
  532. *know what you are doing.
  533. */
  534. static enum CRStatus
  535. sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
  536. xmlNode * a_node, gboolean * a_result,
  537. gboolean a_eval_sel_list_from_end,
  538. gboolean a_recurse)
  539. {
  540. CRSimpleSel *cur_sel = NULL;
  541. xmlNode *cur_node = NULL;
  542. g_return_val_if_fail (a_this && PRIVATE (a_this)
  543. && a_this && a_node
  544. && a_result, CR_BAD_PARAM_ERROR);
  545. *a_result = FALSE;
  546. if (a_node->type != XML_ELEMENT_NODE)
  547. return CR_OK;
  548. if (a_eval_sel_list_from_end == TRUE) {
  549. /*go and get the last simple selector of the list */
  550. for (cur_sel = a_sel;
  551. cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
  552. } else {
  553. cur_sel = a_sel;
  554. }
  555. for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
  556. if (((cur_sel->type_mask & TYPE_SELECTOR)
  557. && (cur_sel->name
  558. && cur_sel->name->stryng
  559. && cur_sel->name->stryng->str)
  560. && (!strcmp (cur_sel->name->stryng->str,
  561. cur_node->name)))
  562. || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
  563. /*
  564. *this simple selector
  565. *matches the current xml node
  566. *Let's see if the preceding
  567. *simple selectors also match
  568. *their xml node counterpart.
  569. */
  570. if (cur_sel->add_sel) {
  571. if (additional_selector_matches_node (a_this, cur_sel->add_sel,
  572. cur_node) == TRUE) {
  573. goto walk_a_step_in_expr;
  574. } else {
  575. goto done;
  576. }
  577. } else {
  578. goto walk_a_step_in_expr;
  579. }
  580. }
  581. if (!(cur_sel->type_mask & TYPE_SELECTOR)
  582. && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
  583. if (!cur_sel->add_sel) {
  584. goto done;
  585. }
  586. if (additional_selector_matches_node
  587. (a_this, cur_sel->add_sel, cur_node)
  588. == TRUE) {
  589. goto walk_a_step_in_expr;
  590. } else {
  591. goto done;
  592. }
  593. } else {
  594. goto done ;
  595. }
  596. walk_a_step_in_expr:
  597. if (a_recurse == FALSE) {
  598. *a_result = TRUE;
  599. goto done;
  600. }
  601. /*
  602. *here, depending on the combinator of cur_sel
  603. *choose the axis of the xml tree traversal
  604. *and walk one step in the xml tree.
  605. */
  606. if (!cur_sel->prev)
  607. break;
  608. switch (cur_sel->combinator) {
  609. case NO_COMBINATOR:
  610. break;
  611. case COMB_WS: /*descendant selector */
  612. {
  613. xmlNode *n = NULL;
  614. enum CRStatus status = CR_OK;
  615. gboolean matches = FALSE;
  616. /*
  617. *walk the xml tree upward looking for a parent
  618. *node that matches the preceding selector.
  619. */
  620. for (n = cur_node->parent; n; n = n->parent) {
  621. status = sel_matches_node_real
  622. (a_this, cur_sel->prev,
  623. n, &matches, FALSE, TRUE);
  624. if (status != CR_OK)
  625. goto done;
  626. if (matches == TRUE) {
  627. cur_node = n ;
  628. break;
  629. }
  630. }
  631. if (!n) {
  632. /*
  633. *didn't find any ancestor that matches
  634. *the previous simple selector.
  635. */
  636. goto done;
  637. }
  638. /*
  639. *in this case, the preceding simple sel
  640. *will have been interpreted twice, which
  641. *is a cpu and mem waste ... I need to find
  642. *another way to do this. Anyway, this is
  643. *my first attempt to write this function and
  644. *I am a bit clueless.
  645. */
  646. break;
  647. }
  648. case COMB_PLUS:
  649. cur_node = get_prev_element_node (cur_node);
  650. if (!cur_node)
  651. goto done;
  652. break;
  653. case COMB_GT:
  654. cur_node = get_next_parent_element_node (cur_node);
  655. if (!cur_node)
  656. goto done;
  657. break;
  658. default:
  659. goto done;
  660. }
  661. continue;
  662. }
  663. /*
  664. *if we reached this point, it means the selector matches
  665. *the xml node.
  666. */
  667. *a_result = TRUE;
  668. done:
  669. return CR_OK;
  670. }
  671. /**
  672. *Returns array of the ruleset statements that matches the
  673. *given xml node.
  674. *The engine keeps in memory the last statement he
  675. *visited during the match. So, the next call
  676. *to this function will eventually return a rulesets list starting
  677. *from the last ruleset statement visited during the previous call.
  678. *The enable users to get matching rulesets in an incremental way.
  679. *Note that for each statement returned,
  680. *the engine calculates the specificity of the selector
  681. *that matched the xml node and stores it in the "specifity" field
  682. *of the statement structure.
  683. *
  684. *@param a_sel_eng the current selection engine
  685. *@param a_node the xml node for which the request
  686. *is being made.
  687. *@param a_sel_list the list of selectors to perform the search in.
  688. *@param a_rulesets in/out parameter. A pointer to the
  689. *returned array of rulesets statements that match the xml node
  690. *given in parameter. The caller allocates the array before calling this
  691. *function.
  692. *@param a_len in/out parameter the length (in sizeof (#CRStatement*))
  693. *of the returned array.
  694. *(the length of a_rulesets, more precisely).
  695. *The caller must set it to the length of a_ruleset prior to calling this
  696. *function. In return, the function sets it to the length
  697. *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
  698. *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
  699. *of the a_rulesets array. In this case, the first *a_len rulesets found
  700. *are put in a_rulesets, and a further call will return the following
  701. *ruleset(s) following the same principle.
  702. *@return CR_OK if all the rulesets found have been returned. In this
  703. *case, *a_len is set to the actual number of ruleset found.
  704. *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
  705. *bad (e.g null pointer).
  706. *@return CR_ERROR if any other error occured.
  707. */
  708. static enum CRStatus
  709. cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
  710. CRStyleSheet * a_stylesheet,
  711. xmlNode * a_node,
  712. CRStatement ** a_rulesets,
  713. gulong * a_len)
  714. {
  715. CRStatement *cur_stmt = NULL;
  716. CRSelector *sel_list = NULL,
  717. *cur_sel = NULL;
  718. gboolean matches = FALSE;
  719. enum CRStatus status = CR_OK;
  720. gulong i = 0;
  721. g_return_val_if_fail (a_this
  722. && a_stylesheet
  723. && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
  724. if (!a_stylesheet->statements) {
  725. *a_rulesets = NULL;
  726. *a_len = 0;
  727. return CR_OK;
  728. }
  729. /*
  730. *if this stylesheet is "new one"
  731. *let's remember it for subsequent calls.
  732. */
  733. if (PRIVATE (a_this)->sheet != a_stylesheet) {
  734. PRIVATE (a_this)->sheet = a_stylesheet;
  735. PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
  736. }
  737. /*
  738. *walk through the list of statements and,
  739. *get the selectors list inside the statements that
  740. *contain some, and try to match our xml node in these
  741. *selectors lists.
  742. */
  743. for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
  744. (PRIVATE (a_this)->cur_stmt = cur_stmt);
  745. cur_stmt = cur_stmt->next) {
  746. /*
  747. *initialyze the selector list in which we will
  748. *really perform the search.
  749. */
  750. sel_list = NULL;
  751. /*
  752. *get the the damn selector list in
  753. *which we have to look
  754. */
  755. switch (cur_stmt->type) {
  756. case RULESET_STMT:
  757. if (cur_stmt->kind.ruleset
  758. && cur_stmt->kind.ruleset->sel_list) {
  759. sel_list = cur_stmt->kind.ruleset->sel_list;
  760. }
  761. break;
  762. case AT_MEDIA_RULE_STMT:
  763. if (cur_stmt->kind.media_rule
  764. && cur_stmt->kind.media_rule->rulesets
  765. && cur_stmt->kind.media_rule->rulesets->
  766. kind.ruleset
  767. && cur_stmt->kind.media_rule->rulesets->
  768. kind.ruleset->sel_list) {
  769. sel_list =
  770. cur_stmt->kind.media_rule->
  771. rulesets->kind.ruleset->sel_list;
  772. }
  773. break;
  774. case AT_IMPORT_RULE_STMT:
  775. /*
  776. *some recursivity may be needed here.
  777. *I don't like this :(
  778. */
  779. break;
  780. default:
  781. break;
  782. }
  783. if (!sel_list)
  784. continue;
  785. /*
  786. *now, we have a comma separated selector list to look in.
  787. *let's walk it and try to match the xml_node
  788. *on each item of the list.
  789. */
  790. for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
  791. if (!cur_sel->simple_sel)
  792. continue;
  793. status = cr_sel_eng_matches_node
  794. (a_this, cur_sel->simple_sel,
  795. a_node, &matches);
  796. if (status == CR_OK && matches == TRUE) {
  797. /*
  798. *bingo!!! we found one ruleset that
  799. *matches that fucking node.
  800. *lets put it in the out array.
  801. */
  802. if (i < *a_len) {
  803. a_rulesets[i] = cur_stmt;
  804. i++;
  805. /*
  806. *For the cascade computing algorithm
  807. *(which is gonna take place later)
  808. *we must compute the specificity
  809. *(css2 spec chap 6.4.1) of the selector
  810. *that matched the current xml node
  811. *and store it in the css2 statement
  812. *(statement == ruleset here).
  813. */
  814. status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
  815. g_return_val_if_fail (status == CR_OK,
  816. CR_ERROR);
  817. cur_stmt->specificity =
  818. cur_sel->simple_sel->
  819. specificity;
  820. } else
  821. {
  822. *a_len = i;
  823. return CR_OUTPUT_TOO_SHORT_ERROR;
  824. }
  825. }
  826. }
  827. }
  828. /*
  829. *if we reached this point, it means
  830. *we reached the end of stylesheet.
  831. *no need to store any info about the stylesheet
  832. *anymore.
  833. */
  834. g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
  835. PRIVATE (a_this)->sheet = NULL;
  836. *a_len = i;
  837. return CR_OK;
  838. }
  839. static enum CRStatus
  840. put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
  841. {
  842. CRPropList *props = NULL,
  843. *pair = NULL,
  844. *tmp_props = NULL;
  845. CRDeclaration *cur_decl = NULL;
  846. g_return_val_if_fail (a_props && a_stmt
  847. && a_stmt->type == RULESET_STMT
  848. && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
  849. props = *a_props;
  850. for (cur_decl = a_stmt->kind.ruleset->decl_list;
  851. cur_decl; cur_decl = cur_decl->next) {
  852. CRDeclaration *decl;
  853. decl = NULL;
  854. pair = NULL;
  855. if (!cur_decl->property
  856. || !cur_decl->property->stryng
  857. || !cur_decl->property->stryng->str)
  858. continue;
  859. /*
  860. *First, test if the property is not
  861. *already present in our properties list
  862. *If yes, apply the cascading rules to
  863. *compute the precedence. If not, insert
  864. *the property into the list
  865. */
  866. cr_prop_list_lookup_prop (props,
  867. cur_decl->property,
  868. &pair);
  869. if (!pair) {
  870. tmp_props = cr_prop_list_append2
  871. (props, cur_decl->property, cur_decl);
  872. if (tmp_props) {
  873. props = tmp_props;
  874. tmp_props = NULL;
  875. }
  876. continue;
  877. }
  878. /*
  879. *A property with the same name already exists.
  880. *We must apply here
  881. *some cascading rules
  882. *to compute the precedence.
  883. */
  884. cr_prop_list_get_decl (pair, &decl);
  885. g_return_val_if_fail (decl, CR_ERROR);
  886. /*
  887. *first, look at the origin.
  888. *6.4.1 says:
  889. *"for normal declarations,
  890. *author style sheets override user
  891. *style sheets which override
  892. *the default style sheet."
  893. */
  894. if (decl->parent_statement
  895. && decl->parent_statement->parent_sheet
  896. && (decl->parent_statement->parent_sheet->origin
  897. < a_stmt->parent_sheet->origin)) {
  898. /*
  899. *if the already selected declaration
  900. *is marked as being !important the current
  901. *declaration must not overide it
  902. *(unless the already selected declaration
  903. *has an UA origin)
  904. */
  905. if (decl->important == TRUE
  906. && decl->parent_statement->parent_sheet->origin
  907. != ORIGIN_UA) {
  908. continue;
  909. }
  910. tmp_props = cr_prop_list_unlink (props, pair);
  911. if (props) {
  912. cr_prop_list_destroy (pair);
  913. }
  914. props = tmp_props;
  915. tmp_props = NULL;
  916. props = cr_prop_list_append2
  917. (props, cur_decl->property, cur_decl);
  918. continue;
  919. } else if (decl->parent_statement
  920. && decl->parent_statement->parent_sheet
  921. && (decl->parent_statement->
  922. parent_sheet->origin
  923. > a_stmt->parent_sheet->origin)) {
  924. cr_utils_trace_info
  925. ("We should not reach this line\n");
  926. continue;
  927. }
  928. /*
  929. *A property with the same
  930. *name and the same origin already exists.
  931. *shit. This is lasting longer than expected ...
  932. *Luckily, the spec says in 6.4.1:
  933. *"more specific selectors will override
  934. *more general ones"
  935. *and
  936. *"if two rules have the same weight,
  937. *origin and specificity,
  938. *the later specified wins"
  939. */
  940. if (a_stmt->specificity
  941. >= decl->parent_statement->specificity) {
  942. if (decl->important == TRUE)
  943. continue;
  944. props = cr_prop_list_unlink (props, pair);
  945. if (pair) {
  946. cr_prop_list_destroy (pair);
  947. pair = NULL;
  948. }
  949. props = cr_prop_list_append2 (props,
  950. cur_decl->property,
  951. cur_decl);
  952. }
  953. }
  954. /*TODO: this may leak. Check this out */
  955. *a_props = props;
  956. return CR_OK;
  957. }
  958. static void
  959. set_style_from_props (CRStyle * a_style, CRPropList * a_props)
  960. {
  961. CRPropList *cur = NULL;
  962. CRDeclaration *decl = NULL;
  963. for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
  964. cr_prop_list_get_decl (cur, &decl);
  965. cr_style_set_style_from_decl (a_style, decl);
  966. decl = NULL;
  967. }
  968. }
  969. /****************************************
  970. *PUBLIC METHODS
  971. ****************************************/
  972. /**
  973. *Creates a new instance of #CRSelEng.
  974. *@return the newly built instance of #CRSelEng of
  975. *NULL if an error occurs.
  976. */
  977. CRSelEng *
  978. cr_sel_eng_new (void)
  979. {
  980. CRSelEng *result = NULL;
  981. result = g_try_malloc (sizeof (CRSelEng));
  982. if (!result) {
  983. cr_utils_trace_info ("Out of memory");
  984. return NULL;
  985. }
  986. memset (result, 0, sizeof (CRSelEng));
  987. PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
  988. if (!PRIVATE (result)) {
  989. cr_utils_trace_info ("Out of memory");
  990. g_free (result);
  991. return NULL;
  992. }
  993. memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
  994. cr_sel_eng_register_pseudo_class_sel_handler
  995. (result, (guchar *) "first-child",
  996. IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
  997. first_child_pseudo_class_handler);
  998. cr_sel_eng_register_pseudo_class_sel_handler
  999. (result, (guchar *) "lang",
  1000. FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
  1001. lang_pseudo_class_handler);
  1002. return result;
  1003. }
  1004. /**
  1005. *Adds a new handler entry in the handlers entry table.
  1006. *@param a_this the current instance of #CRSelEng
  1007. *@param a_pseudo_class_sel_name the name of the pseudo class selector.
  1008. *@param a_pseudo_class_type the type of the pseudo class selector.
  1009. *@param a_handler the actual handler or callback to be called during
  1010. *the selector evaluation process.
  1011. *@return CR_OK, upon successful completion, an error code otherwise.
  1012. */
  1013. enum CRStatus
  1014. cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
  1015. guchar * a_name,
  1016. enum CRPseudoType a_type,
  1017. CRPseudoClassSelectorHandler
  1018. a_handler)
  1019. {
  1020. struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
  1021. GList *list = NULL;
  1022. g_return_val_if_fail (a_this && PRIVATE (a_this)
  1023. && a_handler && a_name, CR_BAD_PARAM_ERROR);
  1024. handler_entry = g_try_malloc
  1025. (sizeof (struct CRPseudoClassSelHandlerEntry));
  1026. if (!handler_entry) {
  1027. return CR_OUT_OF_MEMORY_ERROR;
  1028. }
  1029. memset (handler_entry, 0,
  1030. sizeof (struct CRPseudoClassSelHandlerEntry));
  1031. handler_entry->name = g_strdup (a_name);
  1032. handler_entry->type = a_type;
  1033. handler_entry->handler = a_handler;
  1034. list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
  1035. if (!list) {
  1036. return CR_OUT_OF_MEMORY_ERROR;
  1037. }
  1038. PRIVATE (a_this)->pcs_handlers = list;
  1039. return CR_OK;
  1040. }
  1041. enum CRStatus
  1042. cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
  1043. guchar * a_name,
  1044. enum CRPseudoType a_type)
  1045. {
  1046. GList *elem = NULL,
  1047. *deleted_elem = NULL;
  1048. gboolean found = FALSE;
  1049. struct CRPseudoClassSelHandlerEntry *entry = NULL;
  1050. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  1051. for (elem = PRIVATE (a_this)->pcs_handlers;
  1052. elem; elem = g_list_next (elem)) {
  1053. entry = elem->data;
  1054. if (!strcmp (entry->name, a_name)
  1055. && entry->type == a_type) {
  1056. found = TRUE;
  1057. break;
  1058. }
  1059. }
  1060. if (found == FALSE)
  1061. return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
  1062. PRIVATE (a_this)->pcs_handlers = g_list_delete_link
  1063. (PRIVATE (a_this)->pcs_handlers, elem);
  1064. entry = elem->data;
  1065. if (entry->name)
  1066. g_free (entry->name);
  1067. g_free (elem);
  1068. g_list_free (deleted_elem);
  1069. return CR_OK;
  1070. }
  1071. /**
  1072. *Unregisters all the pseudo class sel handlers
  1073. *and frees all the associated allocated datastructures.
  1074. *@param a_this the current instance of #CRSelEng .
  1075. *@return CR_OK upon succesful completion, an error code
  1076. *otherwise.
  1077. */
  1078. enum CRStatus
  1079. cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
  1080. {
  1081. GList *elem = NULL;
  1082. struct CRPseudoClassSelHandlerEntry *entry = NULL;
  1083. g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
  1084. if (!PRIVATE (a_this)->pcs_handlers)
  1085. return CR_OK;
  1086. for (elem = PRIVATE (a_this)->pcs_handlers;
  1087. elem; elem = g_list_next (elem)) {
  1088. entry = elem->data;
  1089. if (!entry)
  1090. continue;
  1091. if (entry->name) {
  1092. g_free (entry->name);
  1093. entry->name = NULL;
  1094. }
  1095. g_free (entry);
  1096. elem->data = NULL;
  1097. }
  1098. g_list_free (PRIVATE (a_this)->pcs_handlers);
  1099. PRIVATE (a_this)->pcs_handlers = NULL;
  1100. return CR_OK;
  1101. }
  1102. enum CRStatus
  1103. cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
  1104. guchar * a_name,
  1105. enum CRPseudoType a_type,
  1106. CRPseudoClassSelectorHandler *
  1107. a_handler)
  1108. {
  1109. GList *elem = NULL;
  1110. struct CRPseudoClassSelHandlerEntry *entry = NULL;
  1111. gboolean found = FALSE;
  1112. g_return_val_if_fail (a_this && PRIVATE (a_this)
  1113. && a_name, CR_BAD_PARAM_ERROR);
  1114. for (elem = PRIVATE (a_this)->pcs_handlers;
  1115. elem; elem = g_list_next (elem)) {
  1116. entry = elem->data;
  1117. if (!strcmp (a_name, entry->name)
  1118. && entry->type == a_type) {
  1119. found = TRUE;
  1120. break;
  1121. }
  1122. }
  1123. if (found == FALSE)
  1124. return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
  1125. *a_handler = entry->handler;
  1126. return CR_OK;
  1127. }
  1128. /**
  1129. *Evaluates a chained list of simple selectors (known as a css2 selector).
  1130. *Says wheter if this selector matches the xml node given in parameter or
  1131. *not.
  1132. *@param a_this the selection engine.
  1133. *@param a_sel the simple selector against which the xml node
  1134. *is going to be matched.
  1135. *@param a_node the node against which the selector is going to be matched.
  1136. *@param a_result out parameter. The result of the match. Is set to
  1137. *TRUE if the selector matches the node, FALSE otherwise. This value
  1138. *is considered if and only if this functions returns CR_OK.
  1139. *@return the CR_OK if the selection ran correctly, an error code otherwise.
  1140. */
  1141. enum CRStatus
  1142. cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
  1143. xmlNode * a_node, gboolean * a_result)
  1144. {
  1145. g_return_val_if_fail (a_this && PRIVATE (a_this)
  1146. && a_this && a_node
  1147. && a_result, CR_BAD_PARAM_ERROR);
  1148. if (a_node->type != XML_ELEMENT_NODE) {
  1149. *a_result = FALSE;
  1150. return CR_OK;
  1151. }
  1152. return sel_matches_node_real (a_this, a_sel,
  1153. a_node, a_result,
  1154. TRUE, TRUE);
  1155. }
  1156. /**
  1157. *Returns an array of pointers to selectors that matches
  1158. *the xml node given in parameter.
  1159. *
  1160. *@param a_this the current instance of the selection engine.
  1161. *@param a_sheet the stylesheet that holds the selectors.
  1162. *@param a_node the xml node to consider during the walk thru
  1163. *the stylesheet.
  1164. *@param a_rulesets out parameter. A pointer to an array of
  1165. *rulesets statement pointers. *a_rulesets is allocated by
  1166. *this function and must be freed by the caller. However, the caller
  1167. *must not alter the rulesets statements pointer because they
  1168. *point to statements that are still in the css stylesheet.
  1169. *@param a_len the length of *a_ruleset.
  1170. *@return CR_OK upon sucessfull completion, an error code otherwise.
  1171. */
  1172. enum CRStatus
  1173. cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
  1174. CRStyleSheet * a_sheet,
  1175. xmlNode * a_node,
  1176. CRStatement *** a_rulesets, gulong * a_len)
  1177. {
  1178. CRStatement **stmts_tab = NULL;
  1179. enum CRStatus status = CR_OK;
  1180. gulong tab_size = 0,
  1181. tab_len = 0,
  1182. index = 0;
  1183. gushort stmts_chunck_size = 8;
  1184. g_return_val_if_fail (a_this
  1185. && a_sheet
  1186. && a_node
  1187. && a_rulesets && *a_rulesets == NULL
  1188. && a_len, CR_BAD_PARAM_ERROR);
  1189. stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
  1190. if (!stmts_tab) {
  1191. cr_utils_trace_info ("Out of memory");
  1192. status = CR_ERROR;
  1193. goto error;
  1194. }
  1195. memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
  1196. tab_size = stmts_chunck_size;
  1197. tab_len = tab_size;
  1198. while ((status = cr_sel_eng_get_matched_rulesets_real
  1199. (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
  1200. == CR_OUTPUT_TOO_SHORT_ERROR) {
  1201. stmts_tab = g_try_realloc (stmts_tab,
  1202. (tab_size + stmts_chunck_size)
  1203. * sizeof (CRStatement *));
  1204. if (!stmts_tab) {
  1205. cr_utils_trace_info ("Out of memory");
  1206. status = CR_ERROR;
  1207. goto error;
  1208. }
  1209. tab_size += stmts_chunck_size;
  1210. index += tab_len;
  1211. tab_len = tab_size - index;
  1212. }
  1213. tab_len = tab_size - stmts_chunck_size + tab_len;
  1214. *a_rulesets = stmts_tab;
  1215. *a_len = tab_len;
  1216. return CR_OK;
  1217. error:
  1218. if (stmts_tab) {
  1219. g_free (stmts_tab);
  1220. stmts_tab = NULL;
  1221. }
  1222. *a_len = 0;
  1223. return status;
  1224. }
  1225. enum CRStatus
  1226. cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
  1227. CRCascade * a_cascade,
  1228. xmlNode * a_node,
  1229. CRPropList ** a_props)
  1230. {
  1231. CRStatement **stmts_tab = NULL;
  1232. enum CRStatus status = CR_OK;
  1233. gulong tab_size = 0,
  1234. tab_len = 0,
  1235. i = 0,
  1236. index = 0;
  1237. enum CRStyleOrigin origin = 0;
  1238. gushort stmts_chunck_size = 8;
  1239. CRStyleSheet *sheet = NULL;
  1240. g_return_val_if_fail (a_this
  1241. && a_cascade
  1242. && a_node && a_props, CR_BAD_PARAM_ERROR);
  1243. for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
  1244. sheet = cr_cascade_get_sheet (a_cascade, origin);
  1245. if (!sheet)
  1246. continue;
  1247. if (tab_size - index < 1) {
  1248. stmts_tab = g_try_realloc
  1249. (stmts_tab, (tab_size + stmts_chunck_size)
  1250. * sizeof (CRStatement *));
  1251. if (!stmts_tab) {
  1252. cr_utils_trace_info ("Out of memory");
  1253. status = CR_ERROR;
  1254. goto cleanup;
  1255. }
  1256. tab_size += stmts_chunck_size;
  1257. /*
  1258. *compute the max size left for
  1259. *cr_sel_eng_get_matched_rulesets_real()'s output tab
  1260. */
  1261. tab_len = tab_size - index;
  1262. }
  1263. while ((status = cr_sel_eng_get_matched_rulesets_real
  1264. (a_this, sheet, a_node, stmts_tab + index, &tab_len))
  1265. == CR_OUTPUT_TOO_SHORT_ERROR) {
  1266. stmts_tab = g_try_realloc
  1267. (stmts_tab, (tab_size + stmts_chunck_size)
  1268. * sizeof (CRStatement *));
  1269. if (!stmts_tab) {
  1270. cr_utils_trace_info ("Out of memory");
  1271. status = CR_ERROR;
  1272. goto cleanup;
  1273. }
  1274. tab_size += stmts_chunck_size;
  1275. index += tab_len;
  1276. /*
  1277. *compute the max size left for
  1278. *cr_sel_eng_get_matched_rulesets_real()'s output tab
  1279. */
  1280. tab_len = tab_size - index;
  1281. }
  1282. if (status != CR_OK) {
  1283. cr_utils_trace_info ("Error while running "
  1284. "selector engine");
  1285. goto cleanup;
  1286. }
  1287. index += tab_len;
  1288. tab_len = tab_size - index;
  1289. }
  1290. /*
  1291. *TODO, walk down the stmts_tab and build the
  1292. *property_name/declaration hashtable.
  1293. *Make sure one can walk from the declaration to
  1294. *the stylesheet.
  1295. */
  1296. for (i = 0; i < index; i++) {
  1297. CRStatement *stmt = stmts_tab[i];
  1298. if (!stmt)
  1299. continue;
  1300. switch (stmt->type) {
  1301. case RULESET_STMT:
  1302. if (!stmt->parent_sheet)
  1303. continue;
  1304. status = put_css_properties_in_props_list
  1305. (a_props, stmt);
  1306. break;
  1307. default:
  1308. break;
  1309. }
  1310. }
  1311. status = CR_OK ;
  1312. cleanup:
  1313. if (stmts_tab) {
  1314. g_free (stmts_tab);
  1315. stmts_tab = NULL;
  1316. }
  1317. return status;
  1318. }
  1319. enum CRStatus
  1320. cr_sel_eng_get_matched_style (CRSelEng * a_this,
  1321. CRCascade * a_cascade,
  1322. xmlNode * a_node,
  1323. CRStyle * a_parent_style,
  1324. CRStyle ** a_style,
  1325. gboolean a_set_props_to_initial_values)
  1326. {
  1327. enum CRStatus status = CR_OK;
  1328. CRPropList *props = NULL;
  1329. g_return_val_if_fail (a_this && a_cascade
  1330. && a_node && a_style, CR_BAD_PARAM_ERROR);
  1331. status = cr_sel_eng_get_matched_properties_from_cascade
  1332. (a_this, a_cascade, a_node, &props);
  1333. g_return_val_if_fail (status == CR_OK, status);
  1334. if (props) {
  1335. if (!*a_style) {
  1336. *a_style = cr_style_new (a_set_props_to_initial_values) ;
  1337. g_return_val_if_fail (*a_style, CR_ERROR);
  1338. } else {
  1339. if (a_set_props_to_initial_values == TRUE) {
  1340. cr_style_set_props_to_initial_values (*a_style) ;
  1341. } else {
  1342. cr_style_set_props_to_default_values (*a_style);
  1343. }
  1344. }
  1345. (*a_style)->parent_style = a_parent_style;
  1346. set_style_from_props (*a_style, props);
  1347. if (props) {
  1348. cr_prop_list_destroy (props);
  1349. props = NULL;
  1350. }
  1351. }
  1352. return CR_OK;
  1353. }
  1354. /**
  1355. *The destructor of #CRSelEng
  1356. *@param a_this the current instance of the selection engine.
  1357. */
  1358. void
  1359. cr_sel_eng_destroy (CRSelEng * a_this)
  1360. {
  1361. g_return_if_fail (a_this);
  1362. if (!PRIVATE (a_this))
  1363. goto end ;
  1364. if (PRIVATE (a_this)->pcs_handlers) {
  1365. cr_sel_eng_unregister_all_pseudo_class_sel_handlers
  1366. (a_this) ;
  1367. PRIVATE (a_this)->pcs_handlers = NULL ;
  1368. }
  1369. g_free (PRIVATE (a_this));
  1370. PRIVATE (a_this) = NULL;
  1371. end:
  1372. if (a_this) {
  1373. g_free (a_this);
  1374. }
  1375. }