diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 7f014501528c4..a0ce2f48c2e83 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -38,9 +38,6 @@ permissions: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.url || github.run_id }} cancel-in-progress: true -env: - CC: ccache gcc - CXX: ccache g++ jobs: LINUX_X64: if: github.repository_owner == 'php' || github.event_name == 'pull_request' @@ -149,7 +146,7 @@ jobs: if: ${{ !matrix.asan }} uses: ./.github/actions/verify-generated-files MACOS_DEBUG_NTS: - if: github.repository_owner == 'php' || github.event_name == 'pull_request' + if: false strategy: fail-fast: false matrix: @@ -216,6 +213,52 @@ jobs: run: .github/scripts/windows/build.bat - name: Test run: .github/scripts/windows/test.bat + COVERAGE_DEBUG_NTS: + services: + postgres: + image: postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + runs-on: ubuntu-22.04 + steps: + - name: git checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Create MSSQL container + uses: ./.github/actions/setup-mssql + - name: apt + uses: ./.github/actions/apt-x64 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: "${{github.job}}-cov-${{hashFiles('main/php_version.h')}}" + append-timestamp: false + - name: Install gcovr + run: sudo -H pip install gcovr + - name: ./configure + uses: ./.github/actions/configure-x64 + with: + configurationParameters: --enable-debug --disable-zts --enable-gcov + - name: make + run: make -j$(/usr/bin/nproc) >/dev/null + - name: make install + uses: ./.github/actions/install-linux + - name: Setup + uses: ./.github/actions/setup-x64 + # We only test with OpCache, the difference in coverage is negligible + - name: Test OpCache + uses: ./.github/actions/test-linux + with: + jitType: tracing + runTestsParameters: >- + -d zend_extension=opcache.so + -d opcache.enable_cli=1 + - name: Upload Test Coverage to Codecov.io + if: always() + run: bash <(curl -s https://codecov.io/bash) BENCHMARKING: name: BENCHMARKING if: github.repository_owner == 'php' || github.event_name == 'pull_request' diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index f6585f4b60f15..ba32032a32c6f 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -195,6 +195,7 @@ PHP 8.4 INTERNALS UPGRADE NOTES - Removed the "properties" HashTable field from php_libxml_node_object. - Added a way to attached private data to a php_libxml_ref_obj. - Added a way to fix a class type onto php_libxml_ref_obj. + - Added a way to record quirks mode in php_libxml_ref_obj. - Added php_libxml_uses_internal_errors(). e. ext/date diff --git a/codecov.yml b/codecov.yml index e64d19fc4e1cf..a36f7f8f4e0ea 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,11 @@ ignore: - - "ext/dom/lexbor/lexbor" # bundled library - - "ext/pcre/pcre2lib" # bundled library + # bundled libraries + - "ext/dom/lexbor/lexbor/core" + - "ext/dom/lexbor/lexbor/css" + - "ext/dom/lexbor/lexbor/dom" + - "ext/dom/lexbor/lexbor/encoding" + - "ext/dom/lexbor/lexbor/html" + - "ext/dom/lexbor/lexbor/ns" + - "ext/dom/lexbor/lexbor/ports" + - "ext/dom/lexbor/lexbor/tag" + - "ext/pcre/pcre2lib" diff --git a/ext/dom/config.m4 b/ext/dom/config.m4 index b3a92287b3f1c..dbf40da7d74a1 100644 --- a/ext/dom/config.m4 +++ b/ext/dom/config.m4 @@ -22,12 +22,14 @@ if test "$PHP_DOM" != "no"; then $LEXBOR_DIR/encoding/big5.c $LEXBOR_DIR/encoding/decode.c $LEXBOR_DIR/encoding/encode.c $LEXBOR_DIR/encoding/encoding.c $LEXBOR_DIR/encoding/euc_kr.c $LEXBOR_DIR/encoding/gb18030.c $LEXBOR_DIR/encoding/iso_2022_jp_katakana.c $LEXBOR_DIR/encoding/jis0208.c $LEXBOR_DIR/encoding/jis0212.c $LEXBOR_DIR/encoding/range.c $LEXBOR_DIR/encoding/res.c $LEXBOR_DIR/encoding/single.c \ $LEXBOR_DIR/html/encoding.c $LEXBOR_DIR/html/interface.c $LEXBOR_DIR/html/parser.c $LEXBOR_DIR/html/token.c $LEXBOR_DIR/html/token_attr.c $LEXBOR_DIR/html/tokenizer.c $LEXBOR_DIR/html/tree.c \ $LEXBOR_DIR/html/interfaces/anchor_element.c $LEXBOR_DIR/html/interfaces/area_element.c $LEXBOR_DIR/html/interfaces/audio_element.c $LEXBOR_DIR/html/interfaces/base_element.c $LEXBOR_DIR/html/interfaces/body_element.c $LEXBOR_DIR/html/interfaces/br_element.c $LEXBOR_DIR/html/interfaces/button_element.c $LEXBOR_DIR/html/interfaces/canvas_element.c $LEXBOR_DIR/html/interfaces/data_element.c $LEXBOR_DIR/html/interfaces/data_list_element.c $LEXBOR_DIR/html/interfaces/details_element.c $LEXBOR_DIR/html/interfaces/dialog_element.c $LEXBOR_DIR/html/interfaces/directory_element.c $LEXBOR_DIR/html/interfaces/div_element.c $LEXBOR_DIR/html/interfaces/d_list_element.c $LEXBOR_DIR/html/interfaces/document.c $LEXBOR_DIR/html/interfaces/element.c $LEXBOR_DIR/html/interfaces/embed_element.c $LEXBOR_DIR/html/interfaces/field_set_element.c $LEXBOR_DIR/html/interfaces/font_element.c $LEXBOR_DIR/html/interfaces/form_element.c $LEXBOR_DIR/html/interfaces/frame_element.c $LEXBOR_DIR/html/interfaces/frame_set_element.c $LEXBOR_DIR/html/interfaces/head_element.c $LEXBOR_DIR/html/interfaces/heading_element.c $LEXBOR_DIR/html/interfaces/hr_element.c $LEXBOR_DIR/html/interfaces/html_element.c $LEXBOR_DIR/html/interfaces/iframe_element.c $LEXBOR_DIR/html/interfaces/image_element.c $LEXBOR_DIR/html/interfaces/input_element.c $LEXBOR_DIR/html/interfaces/label_element.c $LEXBOR_DIR/html/interfaces/legend_element.c $LEXBOR_DIR/html/interfaces/li_element.c $LEXBOR_DIR/html/interfaces/link_element.c $LEXBOR_DIR/html/interfaces/map_element.c $LEXBOR_DIR/html/interfaces/marquee_element.c $LEXBOR_DIR/html/interfaces/media_element.c $LEXBOR_DIR/html/interfaces/menu_element.c $LEXBOR_DIR/html/interfaces/meta_element.c $LEXBOR_DIR/html/interfaces/meter_element.c $LEXBOR_DIR/html/interfaces/mod_element.c $LEXBOR_DIR/html/interfaces/object_element.c $LEXBOR_DIR/html/interfaces/o_list_element.c $LEXBOR_DIR/html/interfaces/opt_group_element.c $LEXBOR_DIR/html/interfaces/option_element.c $LEXBOR_DIR/html/interfaces/output_element.c $LEXBOR_DIR/html/interfaces/paragraph_element.c $LEXBOR_DIR/html/interfaces/param_element.c $LEXBOR_DIR/html/interfaces/picture_element.c $LEXBOR_DIR/html/interfaces/pre_element.c $LEXBOR_DIR/html/interfaces/progress_element.c $LEXBOR_DIR/html/interfaces/quote_element.c $LEXBOR_DIR/html/interfaces/script_element.c $LEXBOR_DIR/html/interfaces/select_element.c $LEXBOR_DIR/html/interfaces/slot_element.c $LEXBOR_DIR/html/interfaces/source_element.c $LEXBOR_DIR/html/interfaces/span_element.c $LEXBOR_DIR/html/interfaces/style_element.c $LEXBOR_DIR/html/interfaces/table_caption_element.c $LEXBOR_DIR/html/interfaces/table_cell_element.c $LEXBOR_DIR/html/interfaces/table_col_element.c $LEXBOR_DIR/html/interfaces/table_element.c $LEXBOR_DIR/html/interfaces/table_row_element.c $LEXBOR_DIR/html/interfaces/table_section_element.c $LEXBOR_DIR/html/interfaces/template_element.c $LEXBOR_DIR/html/interfaces/text_area_element.c $LEXBOR_DIR/html/interfaces/time_element.c $LEXBOR_DIR/html/interfaces/title_element.c $LEXBOR_DIR/html/interfaces/track_element.c $LEXBOR_DIR/html/interfaces/u_list_element.c $LEXBOR_DIR/html/interfaces/unknown_element.c $LEXBOR_DIR/html/interfaces/video_element.c $LEXBOR_DIR/html/interfaces/window.c \ - $LEXBOR_DIR/selectors/selectors.c \ + $LEXBOR_DIR/css/state.c $LEXBOR_DIR/css/log.c $LEXBOR_DIR/css/parser.c $LEXBOR_DIR/css/selectors/state.c $LEXBOR_DIR/css/selectors/selectors.c $LEXBOR_DIR/css/selectors/selector.c $LEXBOR_DIR/css/selectors/pseudo_state.c $LEXBOR_DIR/css/selectors/pseudo.c $LEXBOR_DIR/css/syntax/tokenizer/error.c $LEXBOR_DIR/css/syntax/state.c $LEXBOR_DIR/css/syntax/parser.c $LEXBOR_DIR/css/syntax/syntax.c $LEXBOR_DIR/css/syntax/anb.c $LEXBOR_DIR/css/syntax/tokenizer.c $LEXBOR_DIR/css/syntax/token.c $LEXBOR_DIR/css/css.c \ + $LEXBOR_DIR/selectors-adapted/selectors.c \ $LEXBOR_DIR/ns/ns.c \ $LEXBOR_DIR/tag/tag.c" PHP_NEW_EXTENSION(dom, [php_dom.c attr.c document.c \ xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \ - domexception.c parentnode.c \ + domexception.c \ + parentnode/tree.c parentnode/css_selectors.c \ processinginstruction.c cdatasection.c \ documentfragment.c domimplementation.c \ element.c node.c characterdata.c \ @@ -38,6 +40,7 @@ if test "$PHP_DOM" != "no"; then namednodemap.c xpath_callbacks.c \ $LEXBOR_SOURCES], $ext_shared,,$PHP_LEXBOR_CFLAGS) + PHP_ADD_BUILD_DIR($ext_builddir/parentnode) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/ports/posix/lexbor/core) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/core) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/dom/interfaces) @@ -45,7 +48,10 @@ if test "$PHP_DOM" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/html/tokenizer) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/html/interfaces) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/encoding) - PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/selectors) + PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/selectors) + PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/tokenizer) + PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/css/syntax/tokenizer) + PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/selectors-adapted) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/ns) PHP_ADD_BUILD_DIR($ext_builddir/$LEXBOR_DIR/tag) PHP_SUBST(DOM_SHARED_LIBADD) diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index a70d226d0fa31..c1256fbe79304 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -9,7 +9,7 @@ if (PHP_DOM == "yes") { ) { EXTENSION("dom", "php_dom.c attr.c document.c \ xml_document.c html_document.c xml_serializer.c html5_serializer.c html5_parser.c namespace_compat.c \ - domexception.c parentnode.c processinginstruction.c \ + domexception.c processinginstruction.c \ cdatasection.c documentfragment.c domimplementation.c element.c \ node.c characterdata.c documenttype.c \ entity.c nodelist.c text.c comment.c \ @@ -17,6 +17,7 @@ if (PHP_DOM == "yes") { notation.c xpath.c dom_iterators.c \ namednodemap.c xpath_callbacks.c", null, "-Iext/dom/lexbor"); + ADD_SOURCES("ext/dom/parentnode", "tree.c css_selectors.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/ports/windows_nt/lexbor/core", "memory.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/core", "array_obj.c array.c avl.c bst.c diyfp.c conv.c dobject.c dtoa.c hash.c mem.c mraw.c print.c serialize.c shs.c str.c strtod.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/dom", "interface.c", "dom"); @@ -27,7 +28,11 @@ if (PHP_DOM == "yes") { ADD_SOURCES("ext/dom/lexbor/lexbor/html", "encoding.c interface.c parser.c token.c token_attr.c tokenizer.c tree.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/encoding", "big5.c decode.c encode.c encoding.c euc_kr.c gb18030.c iso_2022_jp_katakana.c jis0208.c jis0212.c range.c res.c single.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/html/interfaces", "anchor_element.c area_element.c audio_element.c base_element.c body_element.c br_element.c button_element.c canvas_element.c data_element.c data_list_element.c details_element.c dialog_element.c directory_element.c div_element.c d_list_element.c document.c element.c embed_element.c field_set_element.c font_element.c form_element.c frame_element.c frame_set_element.c head_element.c heading_element.c hr_element.c html_element.c iframe_element.c image_element.c input_element.c label_element.c legend_element.c li_element.c link_element.c map_element.c marquee_element.c media_element.c menu_element.c meta_element.c meter_element.c mod_element.c object_element.c o_list_element.c opt_group_element.c option_element.c output_element.c paragraph_element.c param_element.c picture_element.c pre_element.c progress_element.c quote_element.c script_element.c select_element.c slot_element.c source_element.c span_element.c style_element.c table_caption_element.c table_cell_element.c table_col_element.c table_element.c table_row_element.c table_section_element.c template_element.c text_area_element.c time_element.c title_element.c track_element.c u_list_element.c unknown_element.c video_element.c window.c", "dom"); - ADD_SOURCES("ext/dom/lexbor/lexbor/selectors", "selectors.c", "dom"); + ADD_SOURCES("ext/dom/lexbor/lexbor/selectors-adapted", "selectors.c", "dom"); + ADD_SOURCES("ext/dom/lexbor/lexbor/css", "state.c log.c parser.c css.c", "dom"); + ADD_SOURCES("ext/dom/lexbor/lexbor/css/selectors", "state.c selectors.c selector.c pseudo_state.c pseudo.c", "dom"); + ADD_SOURCES("ext/dom/lexbor/lexbor/css/syntax", "state.c parser.c syntax.c anb.c tokenizer.c token.c", "dom"); + ADD_SOURCES("ext/dom/lexbor/lexbor/css/syntax/tokenizer", "error.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/ns", "ns.c", "dom"); ADD_SOURCES("ext/dom/lexbor/lexbor/tag", "tag.c", "dom"); ADD_FLAG("CFLAGS_DOM", "/D LEXBOR_STATIC "); diff --git a/ext/dom/domexception.h b/ext/dom/domexception.h index 799b4fd189a63..59be7a55132c5 100644 --- a/ext/dom/domexception.h +++ b/ext/dom/domexception.h @@ -46,4 +46,7 @@ typedef enum { VALIDATION_ERR = 16 } dom_exception_code; +void php_dom_throw_error(int error_code, bool strict_error); +void php_dom_throw_error_with_message(int error_code, char *error_message, bool strict_error); + #endif /* DOM_EXCEPTION_H */ diff --git a/ext/dom/element.c b/ext/dom/element.c index 1b9038ef429a2..89456bf603197 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -1714,4 +1714,66 @@ PHP_METHOD(DOMElement, toggleAttribute) } /* }}} end DOMElement::prepend */ +static void php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS, bool all) +{ + zend_string *selectors_str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(selectors_str) + ZEND_PARSE_PARAMETERS_END(); + + xmlNodePtr thisp; + dom_object *intern; + zval *id; + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern); + + if (all) { + dom_parent_node_query_selector_all(thisp, intern, return_value, selectors_str); + } else { + dom_parent_node_query_selector(thisp, intern, return_value, selectors_str); + } +} + +PHP_METHOD(DOM_Element, querySelector) +{ + php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +PHP_METHOD(DOM_Element, querySelectorAll) +{ + php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +PHP_METHOD(DOM_Element, matches) +{ + zend_string *selectors_str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(selectors_str) + ZEND_PARSE_PARAMETERS_END(); + + xmlNodePtr thisp; + dom_object *intern; + zval *id; + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern); + + dom_element_matches(thisp, intern, return_value, selectors_str); +} + +PHP_METHOD(DOM_Element, closest) +{ + zend_string *selectors_str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(selectors_str) + ZEND_PARSE_PARAMETERS_END(); + + xmlNodePtr thisp; + dom_object *intern; + zval *id; + DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern); + + dom_element_closest(thisp, intern, return_value, selectors_str); +} + #endif diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 68bb9bcc69744..06d6fc35d1c03 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -24,6 +24,7 @@ #include "html5_parser.h" #include #include +#include #include #include #include @@ -379,6 +380,7 @@ void lexbor_libxml2_bridge_copy_observations(lxb_html_tree_t *tree, lexbor_libxm observations->has_explicit_html_tag = tree->has_explicit_html_tag; observations->has_explicit_head_tag = tree->has_explicit_head_tag; observations->has_explicit_body_tag = tree->has_explicit_body_tag; + observations->quirks_mode = lxb_dom_interface_document(tree->document)->compat_mode == LXB_DOM_DOCUMENT_CMODE_QUIRKS; } #endif /* HAVE_LIBXML && HAVE_DOM */ diff --git a/ext/dom/html5_parser.h b/ext/dom/html5_parser.h index bffa6b0c8aaab..6b01a187605e4 100644 --- a/ext/dom/html5_parser.h +++ b/ext/dom/html5_parser.h @@ -47,6 +47,7 @@ typedef struct _lexbor_libxml2_bridge_extracted_observations { bool has_explicit_html_tag; bool has_explicit_head_tag; bool has_explicit_body_tag; + bool quirks_mode; } lexbor_libxml2_bridge_extracted_observations; typedef struct _lexbor_libxml2_bridge_parse_context { diff --git a/ext/dom/html_document.c b/ext/dom/html_document.c index fe0a447fbb29d..a92673d2909a9 100644 --- a/ext/dom/html_document.c +++ b/ext/dom/html_document.c @@ -917,6 +917,7 @@ PHP_METHOD(DOM_HTMLDocument, createFromString) NULL ); intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->quirks_mode = ctx.observations.quirks_mode; intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); return; @@ -1137,6 +1138,7 @@ PHP_METHOD(DOM_HTMLDocument, createFromFile) NULL ); intern->document->class_type = PHP_LIBXML_CLASS_MODERN; + intern->document->quirks_mode = ctx.observations.quirks_mode; intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper); return; diff --git a/ext/dom/lexbor/lexbor/core/array.c b/ext/dom/lexbor/lexbor/core/array.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/array.h b/ext/dom/lexbor/lexbor/core/array.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/array_obj.c b/ext/dom/lexbor/lexbor/core/array_obj.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/array_obj.h b/ext/dom/lexbor/lexbor/core/array_obj.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/avl.c b/ext/dom/lexbor/lexbor/core/avl.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/avl.h b/ext/dom/lexbor/lexbor/core/avl.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/bst.c b/ext/dom/lexbor/lexbor/core/bst.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/bst.h b/ext/dom/lexbor/lexbor/core/bst.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/bst_map.c b/ext/dom/lexbor/lexbor/core/bst_map.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/bst_map.h b/ext/dom/lexbor/lexbor/core/bst_map.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/def.h b/ext/dom/lexbor/lexbor/core/def.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/diyfp.c b/ext/dom/lexbor/lexbor/core/diyfp.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/diyfp.h b/ext/dom/lexbor/lexbor/core/diyfp.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/dobject.c b/ext/dom/lexbor/lexbor/core/dobject.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/dobject.h b/ext/dom/lexbor/lexbor/core/dobject.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/dtoa.c b/ext/dom/lexbor/lexbor/core/dtoa.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/dtoa.h b/ext/dom/lexbor/lexbor/core/dtoa.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/fs.h b/ext/dom/lexbor/lexbor/core/fs.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/in.c b/ext/dom/lexbor/lexbor/core/in.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/in.h b/ext/dom/lexbor/lexbor/core/in.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/lexbor.h b/ext/dom/lexbor/lexbor/core/lexbor.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/mem.c b/ext/dom/lexbor/lexbor/core/mem.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/mem.h b/ext/dom/lexbor/lexbor/core/mem.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/mraw.c b/ext/dom/lexbor/lexbor/core/mraw.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/mraw.h b/ext/dom/lexbor/lexbor/core/mraw.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/plog.c b/ext/dom/lexbor/lexbor/core/plog.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/plog.h b/ext/dom/lexbor/lexbor/core/plog.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/sbst.h b/ext/dom/lexbor/lexbor/core/sbst.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/shs.c b/ext/dom/lexbor/lexbor/core/shs.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/shs.h b/ext/dom/lexbor/lexbor/core/shs.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/str.c b/ext/dom/lexbor/lexbor/core/str.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/str.h b/ext/dom/lexbor/lexbor/core/str.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/str_res.h b/ext/dom/lexbor/lexbor/core/str_res.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/strtod.c b/ext/dom/lexbor/lexbor/core/strtod.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/strtod.h b/ext/dom/lexbor/lexbor/core/strtod.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/types.h b/ext/dom/lexbor/lexbor/core/types.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/utils.c b/ext/dom/lexbor/lexbor/core/utils.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/core/utils.h b/ext/dom/lexbor/lexbor/core/utils.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/css/css.c b/ext/dom/lexbor/lexbor/css/css.c new file mode 100644 index 0000000000000..3b6f0cb0f2f45 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/css.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2021-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" + + +typedef struct { + lexbor_str_t *str; + lexbor_mraw_t *mraw; +} +lxb_css_str_ctx_t; + + +static lxb_status_t +lxb_css_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx); + + +lxb_css_memory_t * +lxb_css_memory_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_memory_t)); +} + +lxb_status_t +lxb_css_memory_init(lxb_css_memory_t *memory, size_t prepare_count) +{ + lxb_status_t status; + + static const size_t size_mem = lexbor_max(sizeof(lxb_css_selector_t), + sizeof(lxb_css_selector_list_t)); + + if (memory == NULL) { + return LXB_STATUS_ERROR_INCOMPLETE_OBJECT; + } + + if (prepare_count < 64) { + prepare_count = 64; + } + + if (memory->objs == NULL) { + memory->objs = lexbor_dobject_create(); + status = lexbor_dobject_init(memory->objs, prepare_count, size_mem); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + if (memory->tree == NULL) { + prepare_count = prepare_count * 96; + + memory->tree = lexbor_mraw_create(); + status = lexbor_mraw_init(memory->tree, prepare_count); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + if (memory->mraw == NULL) { + memory->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(memory->mraw, 4096); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + memory->ref_count = 1; + + return LXB_STATUS_OK; + +failed: + + (void) lxb_css_memory_destroy(memory, false); + + return status; +} + +void +lxb_css_memory_clean(lxb_css_memory_t *memory) +{ + if (memory->objs != NULL) { + lexbor_dobject_clean(memory->objs); + } + + if (memory->mraw != NULL) { + lexbor_mraw_clean(memory->mraw); + } + + if (memory->tree != NULL) { + lexbor_mraw_clean(memory->tree); + } +} + +lxb_css_memory_t * +lxb_css_memory_destroy(lxb_css_memory_t *memory, bool self_destroy) +{ + if (memory == NULL) { + return NULL; + } + + if (memory->objs != NULL) { + memory->objs = lexbor_dobject_destroy(memory->objs, true); + } + + if (memory->mraw != NULL) { + memory->mraw = lexbor_mraw_destroy(memory->mraw, true); + } + + if (memory->tree != NULL) { + memory->tree = lexbor_mraw_destroy(memory->tree, true); + } + + if (self_destroy) { + return lexbor_free(memory); + } + + return memory; +} + +lxb_css_memory_t * +lxb_css_memory_ref_inc(lxb_css_memory_t *memory) +{ + if (SIZE_MAX - memory->ref_count == 0) { + return NULL; + } + + memory->ref_count++; + + return memory; +} + +void +lxb_css_memory_ref_dec(lxb_css_memory_t *memory) +{ + if (memory->ref_count > 0) { + memory->ref_count--; + } +} + +lxb_css_memory_t * +lxb_css_memory_ref_dec_destroy(lxb_css_memory_t *memory) +{ + if (memory->ref_count > 0) { + memory->ref_count--; + } + + if (memory->ref_count == 0) { + return lxb_css_memory_destroy(memory, true); + } + + return memory; +} + +lxb_status_t +lxb_css_make_data(lxb_css_parser_t *parser, lexbor_str_t *str, + uintptr_t begin, uintptr_t end) +{ + size_t length, offlen, len; + const lxb_char_t *pos; + const lexbor_str_t *tmp; + + tmp = &parser->str; + + offlen = begin - parser->offset; + length = end - begin; + + if (str->data == NULL) { + (void) lexbor_str_init(str, parser->memory->mraw, length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + if (tmp->length > offlen) { + len = tmp->length - offlen; + + if (len >= length) { + memcpy(str->data + str->length, tmp->data + offlen, length); + goto done; + } + else { + memcpy(str->data + str->length, tmp->data + offlen, len); + } + + str->length += len; + + pos = parser->pos; + length -= len; + } + else { + pos = parser->pos + (offlen - tmp->length); + } + + memcpy(str->data + str->length, pos, length); + +done: + + str->length += length; + str->data[str->length] = '\0'; + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_serialize_char_handler(const void *style, lxb_css_style_serialize_f cb, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = cb(style, lexbor_serialize_length_cb, &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = cb(style, lexbor_serialize_copy_cb, &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_serialize_str_handler(const void *style, lexbor_str_t *str, + lexbor_mraw_t *mraw, + lxb_css_style_serialize_f cb) +{ + lxb_css_str_ctx_t ctx; + + ctx.str = str; + ctx.mraw = mraw; + + if (str->data == NULL) { + lexbor_str_init(str, mraw, 1); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + return cb(style, lxb_css_str_cb, &ctx); +} + +static lxb_status_t +lxb_css_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx) +{ + lxb_char_t *ptr; + lxb_css_str_ctx_t *ctx = (lxb_css_str_ctx_t *) cb_ctx; + + ptr = lexbor_str_append(ctx->str, ctx->mraw, data, len); + + return (ptr != NULL) ? LXB_STATUS_OK : LXB_STATUS_ERROR_MEMORY_ALLOCATION; +} diff --git a/ext/dom/lexbor/lexbor/css/log.c b/ext/dom/lexbor/lexbor/css/log.c new file mode 100644 index 0000000000000..e9b244a450dab --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/log.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/print.h" +#include "lexbor/core/serialize.h" +#include "lexbor/css/log.h" + + +typedef struct { + const char *msg; + size_t length; +} +lxb_css_log_type_str_t; + + +static const lxb_css_log_type_str_t lxb_css_log_types_map[] = { + {"Info", 4}, + {"Warning", 7}, + {"Error", 5}, + {"Syntax error", 12} +}; + + +lxb_css_log_t * +lxb_css_log_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_log_t)); +} + +lxb_status_t +lxb_css_log_init(lxb_css_log_t *log, lexbor_mraw_t *mraw) +{ + lxb_status_t status; + + if (log == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + status = lexbor_array_obj_init(&log->messages, 64, + sizeof(lxb_css_log_message_t)); + if (status != LXB_STATUS_OK) { + memset(&log->mraw, 0, sizeof(lexbor_mraw_t)); + return status; + } + + if (mraw != NULL) { + log->mraw = mraw; + log->self_mraw = false; + return LXB_STATUS_OK; + } + + log->self_mraw = true; + + log->mraw = lexbor_mraw_create(); + + return lexbor_mraw_init(log->mraw, 4096); +} + +void +lxb_css_log_clean(lxb_css_log_t *log) +{ + if (log != NULL) { + lexbor_array_obj_clean(&log->messages); + + if (log->self_mraw) { + lexbor_mraw_clean(log->mraw); + } + } +} + +lxb_css_log_t * +lxb_css_log_destroy(lxb_css_log_t *log, bool self_destroy) +{ + if (log == NULL) { + return NULL; + } + + (void) lexbor_array_obj_destroy(&log->messages, false); + + if (log->self_mraw) { + (void) lexbor_mraw_destroy(log->mraw, true); + } + + if (self_destroy) { + log = lexbor_free(log); + } + + return log; +} + +lxb_css_log_message_t * +lxb_css_log_append(lxb_css_log_t *log, lxb_css_log_type_t type, + const lxb_char_t *str, size_t length) +{ + lxb_css_log_message_t *msg; + + msg = lexbor_array_obj_push(&log->messages); + if (msg == NULL) { + return NULL; + } + + if (lexbor_str_init(&msg->text, log->mraw, length) == NULL) { + lexbor_array_obj_pop(&log->messages); + return NULL; + } + + memcpy(msg->text.data, str, length); + msg->text.length = length; + + msg->text.data[length] = '\0'; + + msg->type = type; + + return msg; +} + +lxb_css_log_message_t * +lxb_css_log_push(lxb_css_log_t *log, lxb_css_log_type_t type, size_t length) +{ + lxb_css_log_message_t *msg; + + msg = lexbor_array_obj_push(&log->messages); + if (msg == NULL) { + return NULL; + } + + if (lexbor_str_init(&msg->text, log->mraw, length) == NULL) { + lexbor_array_obj_pop(&log->messages); + return NULL; + } + + msg->type = type; + + return msg; +} + +lxb_css_log_message_t * +lxb_css_log_format(lxb_css_log_t *log, lxb_css_log_type_t type, + const char *format, ...) +{ + size_t psize; + lxb_css_log_message_t *msg; + va_list va; + + va_start(va, format); + psize = lexbor_vprintf_size(format, va); + va_end(va); + + if (psize == LXB_PRINT_ERROR) { + return NULL; + } + + msg = lxb_css_log_push(log, LXB_CSS_LOG_SYNTAX_ERROR, psize); + if (msg == NULL) { + return NULL; + } + + va_start(va, format); + (void) lexbor_vsprintf(msg->text.data, psize, format, va); + va_end(va); + + msg->text.length = psize; + + return msg; +} + +lxb_css_log_message_t * +lxb_css_log_not_supported(lxb_css_log_t *log, + const char *module_name, const char *description) +{ + static const char unexpected[] = "%s. Not supported: %s"; + + return lxb_css_log_format(log, LXB_CSS_LOG_SYNTAX_ERROR, unexpected, + module_name, description); +} + +const lxb_char_t * +lxb_css_log_type_by_id(lxb_css_log_type_t type, size_t *out_length) +{ + if (out_length != NULL) { + *out_length = lxb_css_log_types_map[type].length; + } + + return (const lxb_char_t *) lxb_css_log_types_map[type].msg; +} + +lxb_status_t +lxb_css_log_serialize(lxb_css_log_t *log, lexbor_serialize_cb_f cb, void *ctx, + const lxb_char_t *indent, size_t indent_length) +{ + size_t i; + lxb_status_t status; + lxb_css_log_message_t *msg; + + if (log->messages.length == 0) { + return LXB_STATUS_OK; + } + + i = 0; + + do { + msg = lexbor_array_obj_get(&log->messages, i); + + if (indent != NULL) { + lexbor_serialize_write(cb, indent, indent_length, ctx, status); + } + + status = lxb_css_log_message_serialize(msg, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + i++; + + if (i == log->messages.length) { + break; + } + + lexbor_serialize_write(cb, "\n", 1, ctx, status); + } + while (true); + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_log_serialize_char(lxb_css_log_t *log, size_t *out_length, + const lxb_char_t *indent, size_t indent_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_log_serialize(log, lexbor_serialize_length_cb, &length, + indent, indent_length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_log_serialize(log, lexbor_serialize_copy_cb, &str, + indent, indent_length); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + + +lxb_status_t +lxb_css_log_message_serialize(lxb_css_log_message_t *msg, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *type_name; + + type_name = lxb_css_log_type_by_id(msg->type, &length); + + lexbor_serialize_write(cb, type_name, length, ctx, status); + lexbor_serialize_write(cb, ". ", 2, ctx, status); + lexbor_serialize_write(cb, msg->text.data, msg->text.length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_log_message_serialize_char(lxb_css_log_message_t *msg, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_log_message_serialize(msg, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_log_message_serialize(msg, lexbor_serialize_copy_cb, &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} diff --git a/ext/dom/lexbor/lexbor/css/parser.c b/ext/dom/lexbor/lexbor/css/parser.c new file mode 100644 index 0000000000000..44a6a64e0cb14 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/parser.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/parser.h" +#include "lexbor/css/state.h" +#include "lexbor/css/syntax/syntax.h" + + +lxb_css_parser_t * +lxb_css_parser_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_parser_t)); +} + +lxb_status_t +lxb_css_parser_init(lxb_css_parser_t *parser, lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + static const size_t lxb_rules_length = 128; + static const size_t lxb_states_length = 1024; + + if (parser == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + /* Stack */ + parser->states_begin = lexbor_malloc(sizeof(lxb_css_parser_state_t) + * lxb_states_length); + if (parser->states_begin == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->states = parser->states_begin; + parser->states_end = parser->states_begin + lxb_states_length; + + memset(parser->states, 0x00, sizeof(lxb_css_parser_state_t)); + parser->states->root = true; + + /* Syntax */ + parser->my_tkz = false; + + if (tkz == NULL) { + tkz = lxb_css_syntax_tokenizer_create(); + status = lxb_css_syntax_tokenizer_init(tkz); + if (status != LXB_STATUS_OK) { + return status; + } + + parser->my_tkz = true; + } + + /* Rules */ + parser->rules_begin = lexbor_malloc(sizeof(lxb_css_syntax_rule_t) + * lxb_rules_length); + if (parser->rules_begin == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->rules_end = parser->rules_begin + lxb_rules_length; + parser->rules = parser->rules_begin; + + /* Temp */ + parser->pos = NULL; + parser->str.length = 0; + parser->str_size = 4096; + + parser->str.data = lexbor_malloc(sizeof(lxb_char_t) * parser->str_size); + if (parser->str.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->log = lxb_css_log_create(); + status = lxb_css_log_init(parser->log, NULL); + if (status != LXB_STATUS_OK) { + return status; + } + + parser->tkz = tkz; + parser->types_begin = NULL; + parser->types_pos = NULL; + parser->types_end = NULL; + parser->stage = LXB_CSS_PARSER_CLEAN; + parser->receive_endings = false; + parser->status = LXB_STATUS_OK; + parser->fake_null = false; + + return LXB_STATUS_OK; +} + +void +lxb_css_parser_clean(lxb_css_parser_t *parser) +{ + lxb_css_syntax_tokenizer_clean(parser->tkz); + lxb_css_log_clean(parser->log); + + parser->rules = parser->rules_begin; + parser->states = parser->states_begin; + parser->types_pos = parser->types_begin; + parser->stage = LXB_CSS_PARSER_CLEAN; + parser->status = LXB_STATUS_OK; + parser->pos = NULL; + parser->str.length = 0; + parser->fake_null = false; +} + +void +lxb_css_parser_erase(lxb_css_parser_t *parser) +{ + lxb_css_parser_clean(parser); + + if (parser->memory != NULL) { + lxb_css_memory_clean(parser->memory); + } +} + +lxb_css_parser_t * +lxb_css_parser_destroy(lxb_css_parser_t *parser, bool self_destroy) +{ + if (parser == NULL) { + return NULL; + } + + if (parser->my_tkz) { + parser->tkz = lxb_css_syntax_tokenizer_destroy(parser->tkz); + } + + parser->log = lxb_css_log_destroy(parser->log, true); + + if (parser->rules_begin != NULL) { + parser->rules_begin = lexbor_free(parser->rules_begin); + } + + if (parser->states_begin != NULL) { + parser->states_begin = lexbor_free(parser->states_begin); + } + + if (parser->types_begin != NULL) { + parser->types_begin = lexbor_free(parser->types_begin); + } + + if (parser->str.data != NULL) { + parser->str.data = lexbor_free(parser->str.data); + } + + if (self_destroy) { + return lexbor_free(parser); + } + + return parser; +} + +lxb_css_parser_state_t * +lxb_css_parser_states_push(lxb_css_parser_t *parser, + lxb_css_parser_state_f state, void *ctx, bool root) +{ + size_t length, cur_length; + lxb_css_parser_state_t *states = ++parser->states; + + if (states >= parser->states_end) { + cur_length = states - parser->states_begin; + + if (SIZE_MAX - cur_length < 1024) { + goto memory_error; + } + + length = cur_length + 1024; + + states = lexbor_realloc(parser->states_begin, + length * sizeof(lxb_css_parser_state_t)); + if (states == NULL) { + goto memory_error; + } + + parser->states_begin = states; + parser->states_end = states + length; + parser->states = states + cur_length; + + states = parser->states; + } + + states->state = state; + states->context = ctx; + states->root = root; + + return states; + +memory_error: + + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + + return NULL; +} + +lxb_css_parser_state_t * +lxb_css_parser_states_next(lxb_css_parser_t *parser, + lxb_css_parser_state_f next, + lxb_css_parser_state_f back, void *ctx, bool root) +{ + lxb_css_parser_state_t *state; + + state = lxb_css_parser_states_push(parser, back, ctx, root); + if (state == NULL) { + return NULL; + } + + parser->rules->state = next; + + return state; +} + +lxb_status_t +lxb_css_parser_types_push(lxb_css_parser_t *parser, + lxb_css_syntax_token_type_t type) +{ + size_t length, new_length; + lxb_css_syntax_token_type_t *tmp; + + if (parser->types_pos >= parser->types_end) { + length = parser->types_end - parser->types_begin; + + if ((SIZE_MAX - length) < 1024) { + return LXB_STATUS_ERROR_OVERFLOW; + } + + new_length = length + 1024; + + tmp = lexbor_realloc(parser->types_begin, + new_length * sizeof(lxb_css_syntax_token_type_t)); + if (tmp == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->types_begin = tmp; + parser->types_end = tmp + new_length; + parser->types_pos = parser->types_begin + length; + } + + *parser->types_pos++ = type; + + return LXB_STATUS_OK; +} + +bool +lxb_css_parser_stop(lxb_css_parser_t *parser) +{ + parser->loop = false; + return true; +} + +bool +lxb_css_parser_fail(lxb_css_parser_t *parser, lxb_status_t status) +{ + parser->status = status; + parser->loop = false; + return true; +} + +bool +lxb_css_parser_unexpected(lxb_css_parser_t *parser) +{ + (void) lxb_css_parser_unexpected_status(parser); + return true; +} + +bool +lxb_css_parser_success(lxb_css_parser_t *parser) +{ + parser->rules->state = lxb_css_state_success; + return true; +} + +bool +lxb_css_parser_failed(lxb_css_parser_t *parser) +{ + lxb_css_syntax_rule_t *rule = parser->rules; + + rule->state = rule->cbx.cb->failed; + rule->failed = true; + + return true; +} + +lxb_status_t +lxb_css_parser_unexpected_status(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + parser->rules->failed = true; + + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +bool +lxb_css_parser_unexpected_data(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + static const char selectors[] = "Selectors"; + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + if (lxb_css_syntax_token_error(parser, token, selectors) == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return true; +} + +lxb_status_t +lxb_css_parser_unexpected_data_status(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + static const char selectors[] = "Selectors"; + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + if (lxb_css_syntax_token_error(parser, token, selectors) == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +bool +lxb_css_parser_memory_fail(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + parser->loop = false; + return true; +} + +lxb_status_t +lxb_css_parser_memory_fail_status(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + parser->loop = false; + + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; +} diff --git a/ext/dom/lexbor/lexbor/css/rule.h b/ext/dom/lexbor/lexbor/css/rule.h index f68491ee77eb1..bd191f9b65192 100644 --- a/ext/dom/lexbor/lexbor/css/rule.h +++ b/ext/dom/lexbor/lexbor/css/rule.h @@ -339,15 +339,15 @@ lxb_css_rule_ref_dec(lxb_css_rule_t *rule) lxb_inline void lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) { +#if 0 if (rule->ref_count > 0) { rule->ref_count--; } if (rule->ref_count == 0) { -#if 0 (void) lxb_css_rule_destroy(rule, true); -#endif } +#endif } lxb_inline void diff --git a/ext/dom/lexbor/lexbor/css/selectors/pseudo.c b/ext/dom/lexbor/lexbor/css/selectors/pseudo.c new file mode 100644 index 0000000000000..0468682e7d02a --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/selectors/pseudo.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/state.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/pseudo_res.h" + + +const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_class_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_class_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_name(const lxb_char_t *name, + size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_class_function_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_id(unsigned id) +{ + return &lxb_css_selectors_pseudo_data_pseudo_class_function[id]; +} + +const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_element_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_element_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_name(const lxb_char_t *name, + size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_element_function_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_id(unsigned id) +{ + return &lxb_css_selectors_pseudo_data_pseudo_element_function[id]; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_function_by_id(unsigned id, bool is_class) +{ + if (is_class) { + return &lxb_css_selectors_pseudo_data_pseudo_class_function[id]; + } + + return &lxb_css_selectors_pseudo_data_pseudo_element_function[id]; +} + +bool +lxb_css_selector_pseudo_function_can_empty(unsigned id, bool is_class) +{ + if (is_class) { + return lxb_css_selectors_pseudo_data_pseudo_class_function[id].empty; + } + + return lxb_css_selectors_pseudo_data_pseudo_element_function[id].empty; +} diff --git a/ext/dom/lexbor/lexbor/css/selectors/pseudo_state.c b/ext/dom/lexbor/lexbor/css/selectors/pseudo_state.c new file mode 100644 index 0000000000000..f40b089d13004 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/selectors/pseudo_state.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/selectors.h" + + +static bool +lxb_css_selectors_state_pseudo_anb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static bool +lxb_css_selectors_state_pseudo_of_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_selectors_state_pseudo_of_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static bool +lxb_css_selectors_state_pseudo_of_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_comp = { + .state = lxb_css_selectors_state_complex_list, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_state_pseudo_of_end +}; + + +lxb_inline bool +lxb_css_selectors_state_pseudo_anb_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + (void) lxb_css_selectors_state_pseudo_anb(parser, token, ctx); + if (parser->status != LXB_STATUS_OK) { + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return lxb_css_parser_failed(parser); + } + + parser->selectors->list = NULL; + + return lxb_css_parser_success(parser); +} + + +bool +lxb_css_selectors_state_pseudo_class_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_current(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_dir(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_has(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_relative_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_is(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_lang(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_not(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_of_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_of_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_where(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_element_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return true; +} + +static bool +lxb_css_selectors_state_pseudo_anb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *list; + lxb_css_selector_anb_of_t *anbof; + + selectors = parser->selectors; + + anbof = lexbor_mraw_alloc(parser->memory->mraw, + sizeof(lxb_css_selector_anb_of_t)); + if (anbof == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + parser->status = lxb_css_syntax_anb_handler(parser, token, &anbof->anb); + if (parser->status != LXB_STATUS_OK) { + lexbor_mraw_free(parser->memory->mraw, anbof); + return true; + } + + list = selectors->list_last; + list->last->u.pseudo.data = anbof; + + anbof->of = NULL; + + return true; +} + +static bool +lxb_css_selectors_state_pseudo_of_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_syntax_rule_t *rule; + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *list; + lxb_css_syntax_token_ident_t *ident; + + static const lxb_char_t of[] = "of"; + + selectors = parser->selectors; + + (void) lxb_css_selectors_state_pseudo_anb(parser, token, ctx); + if (parser->status != LXB_STATUS_OK) { + selectors->list = NULL; + selectors->list_last = NULL; + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + if (lxb_css_syntax_token_error(parser, token, "Selectors") == NULL) { + return lxb_css_parser_memory_fail(parser); + } + } + + return lxb_css_parser_failed(parser); + } + + list = selectors->list_last; + + selectors->list = NULL; + + lxb_css_parser_token_wo_ws_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + ident = lxb_css_syntax_token_ident(token); + + if (ident->length == sizeof(of) - 1 + && lexbor_str_data_ncasecmp(ident->data, of, ident->length)) + { + lxb_css_syntax_token_consume(parser->tkz); + + selectors->list = NULL; + selectors->list_last = NULL; + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + rule = lxb_css_syntax_parser_components_push(parser, token, + lxb_css_selectors_state_pseudo_of_back, + &lxb_css_selectors_comp, list, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + if (rule == NULL) { + lexbor_mraw_free(parser->memory->mraw, + list->last->u.pseudo.data); + return lxb_css_parser_memory_fail(parser); + } + + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_complex_list); + return true; + } + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_of_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_selector_anb_of_t *anbof; + lxb_css_selector_list_t *list = ctx; + + anbof = list->last->u.pseudo.data; + anbof->of = parser->selectors->list; + + parser->selectors->list = NULL; + + return LXB_STATUS_OK; +} + +static bool +lxb_css_selectors_state_pseudo_of_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + parser->selectors->list = NULL; + + return lxb_css_parser_success(parser); +} diff --git a/ext/dom/lexbor/lexbor/css/selectors/selector.c b/ext/dom/lexbor/lexbor/css/selectors/selector.c new file mode 100644 index 0000000000000..e8bf96ba88cf7 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/selectors/selector.c @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/serialize.h" +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/selector.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_const.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/state.h" +#include "lexbor/css/selectors/pseudo_res.h" + + +typedef void +(*lxb_css_selector_destroy_f)(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +typedef lxb_status_t +(*lxb_css_selector_serialize_f)(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + + +static void +lxb_css_selector_destroy_undef(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_any(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_id(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_attribute(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_pseudo_class_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_pseudo_element_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); + +static lxb_status_t +lxb_css_selector_serialize_undef(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_any(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_id(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_attribute(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_class_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_element(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_element_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + +static lxb_status_t +lxb_css_selector_serialize_pseudo_single(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx, + bool is_class); + + +static const lxb_css_selector_destroy_f + lxb_selector_destroy_map[LXB_CSS_SELECTOR_TYPE__LAST_ENTRY] = +{ + lxb_css_selector_destroy_undef, + lxb_css_selector_destroy_any, + lxb_css_selector_destroy_any, + lxb_css_selector_destroy_id, + lxb_css_selector_destroy_id, + lxb_css_selector_destroy_attribute, + lxb_css_selector_destroy_undef, + lxb_css_selector_destroy_pseudo_class_function, + lxb_css_selector_destroy_undef, + lxb_css_selector_destroy_pseudo_element_function +}; + +static const lxb_css_selector_serialize_f + lxb_selector_serialize_map[LXB_CSS_SELECTOR_TYPE__LAST_ENTRY] = +{ + lxb_css_selector_serialize_undef, + lxb_css_selector_serialize_any, + lxb_css_selector_serialize_any, + lxb_css_selector_serialize_id, + lxb_css_selector_serialize_class, + lxb_css_selector_serialize_attribute, + lxb_css_selector_serialize_pseudo_class, + lxb_css_selector_serialize_pseudo_class_function, + lxb_css_selector_serialize_pseudo_element, + lxb_css_selector_serialize_pseudo_element_function +}; + + +lxb_css_selector_t * +lxb_css_selector_create(lxb_css_selector_list_t *list) +{ + lxb_css_selector_t *selector = lexbor_dobject_calloc(list->memory->objs); + if (selector == NULL) { + return NULL; + } + + selector->list = list; + + return selector; +} + +void +lxb_css_selector_destroy(lxb_css_selector_t *selector) +{ + lxb_css_memory_t *memory; + + if (selector != NULL) { + memory = selector->list->memory; + + lxb_selector_destroy_map[selector->type](selector, memory); + lexbor_dobject_free(memory->objs, selector); + } +} + +void +lxb_css_selector_destroy_chain(lxb_css_selector_t *selector) +{ + lxb_css_selector_t *next; + + while (selector != NULL) { + next = selector->next; + lxb_css_selector_destroy(selector); + selector = next; + } +} + +void +lxb_css_selector_remove(lxb_css_selector_t *selector) +{ + if (selector->next != NULL) { + selector->next->prev = selector->prev; + } + + if (selector->prev != NULL) { + selector->prev->next = selector->next; + } + + if (selector->list->first == selector) { + selector->list->first = selector->next; + } + + if (selector->list->last == selector) { + selector->list->last = selector->prev; + } +} + +lxb_css_selector_list_t * +lxb_css_selector_list_create(lxb_css_memory_t *mem) +{ + lxb_css_selector_list_t *list; + + list = lexbor_dobject_calloc(mem->objs); + if (list == NULL) { + return NULL; + } + + list->memory = mem; + + return list; +} + +void +lxb_css_selector_list_remove(lxb_css_selector_list_t *list) +{ + if (list->next != NULL) { + list->next->prev = list->prev; + } + + if (list->prev != NULL) { + list->prev->next = list->next; + } +} + +void +lxb_css_selector_list_selectors_remove(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *list) +{ + lxb_css_selector_list_remove(list); + + if (selectors->list == list) { + selectors->list = list->next; + } + + if (selectors->list_last == list) { + selectors->list_last = list->prev; + } +} + +void +lxb_css_selector_list_destroy(lxb_css_selector_list_t *list) +{ + if (list != NULL) { + lxb_css_selector_destroy_chain(list->first); + lexbor_dobject_free(list->memory->objs, list); + } +} + +void +lxb_css_selector_list_destroy_chain(lxb_css_selector_list_t *list) +{ + lxb_css_selector_list_t *next; + + while (list != NULL) { + next = list->next; + lxb_css_selector_list_destroy(list); + list = next; + } +} + +void +lxb_css_selector_list_destroy_memory(lxb_css_selector_list_t *list) +{ + if (list != NULL) { + (void) lxb_css_memory_destroy(list->memory, true); + } +} + +static void +lxb_css_selector_destroy_undef(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + /* Do nothing. */ +} + +static void +lxb_css_selector_destroy_any(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->ns.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->ns.data); + } + + if (selector->name.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->name.data); + } +} + +static void +lxb_css_selector_destroy_id(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->name.data != NULL) { + (void) lexbor_mraw_free(mem->mraw, selector->name.data); + } +} + +static void +lxb_css_selector_destroy_attribute(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->ns.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->ns.data); + } + + if (selector->name.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->name.data); + } + + if (selector->u.attribute.value.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->u.attribute.value.data); + } +} + +static void +lxb_css_selector_destroy_pseudo_class_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + lxb_css_selector_anb_of_t *anbof; + lxb_css_selector_pseudo_t *pseudo; + + pseudo = &selector->u.pseudo; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + anbof = pseudo->data; + + if (anbof != NULL) { + lxb_css_selector_list_destroy_chain(anbof->of); + lexbor_mraw_free(mem->mraw, anbof); + } + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + default: + break; + } +} + +static void +lxb_css_selector_destroy_pseudo_element_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + +} + +lxb_status_t +lxb_css_selector_serialize(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_selector_serialize_map[selector->type](selector, cb, ctx); +} + +lxb_status_t +lxb_css_selector_serialize_chain(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t *data; + lxb_status_t status; + + if (selector == NULL) { + return LXB_STATUS_OK; + } + + if (selector->combinator > LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + data = lxb_css_selector_combinator(selector, &length); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_selector_serialize_write(data, length); + lxb_css_selector_serialize_write(" ", 1); + } + + status = lxb_css_selector_serialize(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + selector = selector->next; + + while (selector != NULL) { + data = lxb_css_selector_combinator(selector, &length); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (length != 0) { + lxb_css_selector_serialize_write(" ", 1); + + if (*data != ' ') { + lxb_css_selector_serialize_write(data, length); + lxb_css_selector_serialize_write(" ", 1); + } + } + + status = lxb_css_selector_serialize(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + selector = selector->next; + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_chain_char(lxb_css_selector_t *selector, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_chain(selector, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_chain(selector, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_selector_serialize_list(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + if (list != NULL) { + return lxb_css_selector_serialize_chain(list->first, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_list_char(lxb_css_selector_list_t *list, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_selector_serialize_list_chain(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + if (list == NULL) { + return LXB_STATUS_OK; + } + + status = lxb_css_selector_serialize_chain(list->first, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + list = list->next; + + while (list != NULL) { + lxb_css_selector_serialize_write(", ", 2); + + status = lxb_css_selector_serialize_chain(list->first, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + list = list->next; + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_list_chain_char(lxb_css_selector_list_t *list, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + + +static lxb_status_t +lxb_css_selector_serialize_undef(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +static lxb_status_t +lxb_css_selector_serialize_any(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + if (selector->ns.data != NULL) { + lxb_css_selector_serialize_write(selector->ns.data, + selector->ns.length); + lxb_css_selector_serialize_write("|", 1); + } + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_id(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + lxb_css_selector_serialize_write("#", 1); + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + lxb_css_selector_serialize_write(".", 1); + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_attribute(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t *p, *begin, *end; + lxb_status_t status; + lxb_css_selector_attribute_t *attr; + + lxb_css_selector_serialize_write("[", 1); + + status = lxb_css_selector_serialize_any(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + attr = &selector->u.attribute; + + if (attr->value.data == NULL) { + return cb((lxb_char_t *) "]", 1, ctx); + } + + switch (attr->match) { + case LXB_CSS_SELECTOR_MATCH_EQUAL: + lxb_css_selector_serialize_write("=", 1); + break; + case LXB_CSS_SELECTOR_MATCH_INCLUDE: + lxb_css_selector_serialize_write("~=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_DASH: + lxb_css_selector_serialize_write("|=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_PREFIX: + lxb_css_selector_serialize_write("^=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_SUFFIX: + lxb_css_selector_serialize_write("$=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_SUBSTRING: + lxb_css_selector_serialize_write("*=", 2); + break; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + p = attr->value.data; + end = attr->value.data + attr->value.length; + + begin = p; + + lxb_css_selector_serialize_write("\"", 1); + + while (p < end) { + if (*p == '"') { + if (begin < p) { + lxb_css_selector_serialize_write(begin, p - begin); + } + + lxb_css_selector_serialize_write("\\000022", 7); + + begin = p + 1; + } + + p++; + } + + if (begin < p) { + lxb_css_selector_serialize_write(begin, p - begin); + } + + lxb_css_selector_serialize_write("\"", 1); + + if (attr->modifier != LXB_CSS_SELECTOR_MODIFIER_UNSET) { + switch (attr->modifier) { + case LXB_CSS_SELECTOR_MODIFIER_I: + lxb_css_selector_serialize_write("i", 1); + break; + + case LXB_CSS_SELECTOR_MODIFIER_S: + lxb_css_selector_serialize_write("s", 1); + break; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + } + + return cb((lxb_char_t *) "]", 1, ctx); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_selector_serialize_pseudo_single(selector, cb, ctx, true); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_class_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + lxb_css_selector_pseudo_t *pseudo; + const lxb_css_selectors_pseudo_data_func_t *pfunc; + + pseudo = &selector->u.pseudo; + + pfunc = &lxb_css_selectors_pseudo_data_pseudo_class_function[pseudo->type]; + + lxb_css_selector_serialize_write(":", 1); + lxb_css_selector_serialize_write(pfunc->name, pfunc->length); + lxb_css_selector_serialize_write("(", 1); + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + status = LXB_STATUS_OK; + + if (pseudo->data != NULL) { + status = lxb_css_selector_serialize_anb_of(pseudo->data, + cb, ctx); + } + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + + default: + status = LXB_STATUS_OK; + break; + } + + if (status != LXB_STATUS_OK) { + return status; + } + + lxb_css_selector_serialize_write(")", 1); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_element(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_selector_serialize_pseudo_single(selector, cb, ctx, false); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_element_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_single(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx, + bool is_class) +{ + lxb_status_t status; + lxb_css_selector_pseudo_t *pseudo; + const lxb_css_selectors_pseudo_data_t *pclass; + + pseudo = &selector->u.pseudo; + + if (is_class) { + pclass = &lxb_css_selectors_pseudo_data_pseudo_class[pseudo->type]; + lxb_css_selector_serialize_write(":", 1); + } + else { + pclass = &lxb_css_selectors_pseudo_data_pseudo_element[pseudo->type]; + lxb_css_selector_serialize_write("::", 2); + } + + lxb_css_selector_serialize_write(pclass->name, pclass->length); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_selector_serialize_anb_of(lxb_css_selector_anb_of_t *anbof, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + static const lxb_char_t of[] = " of "; + + status = lxb_css_syntax_anb_serialize(&anbof->anb, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (anbof->of != NULL) { + lxb_css_selector_serialize_write(of, sizeof(of) - 1); + + return lxb_css_selector_serialize_list_chain(anbof->of, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_combinator(lxb_css_selector_t *selector, size_t *out_length) +{ + switch (selector->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) " "; + + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + if (out_length != NULL) {*out_length = 0;} + return (lxb_char_t *) ""; + + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) ">"; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) "+"; + + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) "~"; + + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + if (out_length != NULL) {*out_length = 2;} + return (lxb_char_t *) "||"; + + default: + if (out_length != NULL) {*out_length = 0;} + return NULL; + } +} + +void +lxb_css_selector_list_append(lxb_css_selector_list_t *list, + lxb_css_selector_t *selector) +{ + selector->prev = list->last; + + if (list->last != NULL) { + list->last->next = selector; + } + else { + list->first = selector; + } + + list->last = selector; +} + +void +lxb_css_selector_append_next(lxb_css_selector_t *dist, lxb_css_selector_t *src) +{ + if (dist->next != NULL) { + dist->next->prev = src; + } + + src->prev = dist; + src->next = dist->next; + + dist->next = src; +} + +void +lxb_css_selector_list_append_next(lxb_css_selector_list_t *dist, + lxb_css_selector_list_t *src) +{ + if (dist->next != NULL) { + dist->next->prev = src; + } + + src->prev = dist; + src->next = dist->next; + + dist->next = src; +} diff --git a/ext/dom/lexbor/lexbor/css/selectors/selectors.c b/ext/dom/lexbor/lexbor/css/selectors/selectors.c new file mode 100644 index 0000000000000..52537873ef080 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/selectors/selectors.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/print.h" +#include "lexbor/css/css.h" + + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_list(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length); + +static lxb_status_t +lxb_css_selectors_components_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_complex_list_cb = { + .state = lxb_css_selectors_state_complex_list, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_compound_list_cb = { + .state = lxb_css_selectors_state_compound_list, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_simple_list_cb = { + .state = lxb_css_selectors_state_simple_list, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_relative_list_cb = { + .state = lxb_css_selectors_state_relative_list, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_complex_cb = { + .state = lxb_css_selectors_state_complex, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_compound_cb = { + .state = lxb_css_selectors_state_compound, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_simple_cb = { + .state = lxb_css_selectors_state_simple, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_relative_cb = { + .state = lxb_css_selectors_state_relative, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_selectors_components_end +}; + + +lxb_css_selectors_t * +lxb_css_selectors_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_selectors_t)); +} + +lxb_status_t +lxb_css_selectors_init(lxb_css_selectors_t *selectors) +{ + if (selectors == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + selectors->list = NULL; + selectors->list_last = NULL; + selectors->parent = NULL; + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->error = 0; + selectors->err_in_function = false; + selectors->failed = false; + + return LXB_STATUS_OK; +} + +void +lxb_css_selectors_clean(lxb_css_selectors_t *selectors) +{ + if (selectors != NULL) { + selectors->list = NULL; + selectors->list_last = NULL; + selectors->parent = NULL; + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->error = 0; + selectors->err_in_function = false; + selectors->failed = false; + } +} + +lxb_css_selectors_t * +lxb_css_selectors_destroy(lxb_css_selectors_t *selectors, bool self_destroy) +{ + if (selectors == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_free(selectors); + } + + return selectors; +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_complex_list(parser, data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_complex_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_complex_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_compound_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_compound_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_simple_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_simple_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_relative_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_relative_list_cb, + data, length); +} + +static lxb_status_t +lxb_css_selectors_parse_prepare(lxb_css_parser_t *parser, + lxb_css_memory_t *memory, + lxb_css_selectors_t *selectors) +{ + if (parser->stage != LXB_CSS_PARSER_CLEAN) { + if (parser->stage == LXB_CSS_PARSER_RUN) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + lxb_css_parser_clean(parser); + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + parser->old_memory = parser->memory; + parser->old_selectors = parser->selectors; + + parser->memory = memory; + parser->selectors = selectors; + + return LXB_STATUS_OK; +} + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_process(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length) +{ + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_buffer_set(parser, data, length); + + rule = lxb_css_syntax_parser_components_push(parser, NULL, NULL, + components, NULL, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + return NULL; + } + + parser->status = lxb_css_syntax_parser_run(parser); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + + return parser->selectors->list; +} + +static void +lxb_css_selectors_parse_finish(lxb_css_parser_t *parser) +{ + parser->stage = LXB_CSS_PARSER_END; + + parser->memory = parser->old_memory; + parser->selectors = parser->old_selectors; +} + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_list(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length) +{ + lxb_css_memory_t *memory; + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *list; + + memory = parser->memory; + selectors = parser->selectors; + + if (selectors == NULL) { + selectors = lxb_css_selectors_create(); + parser->status = lxb_css_selectors_init(selectors); + + if (parser->status != LXB_STATUS_OK) { + (void) lxb_css_selectors_destroy(selectors, true); + return NULL; + } + } + else { + lxb_css_selectors_clean(selectors); + } + + if (memory == NULL) { + memory = lxb_css_memory_create(); + parser->status = lxb_css_memory_init(memory, 256); + + if (parser->status != LXB_STATUS_OK) { + if (selectors != parser->selectors) { + (void) lxb_css_selectors_destroy(selectors, true); + } + + (void) lxb_css_memory_destroy(memory, true); + return NULL; + } + } + + parser->status = lxb_css_selectors_parse_prepare(parser, memory, selectors); + if (parser->status != LXB_STATUS_OK) { + list = NULL; + goto end; + } + + list = lxb_css_selectors_parse_process(parser, components, data, length); + + lxb_css_selectors_parse_finish(parser); + +end: + + if (list == NULL && memory != parser->memory) { + (void) lxb_css_memory_destroy(memory, true); + } + + if (selectors != parser->selectors) { + (void) lxb_css_selectors_destroy(selectors, true); + } + + return list; +} + +static lxb_status_t +lxb_css_selectors_components_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_selector_list_t *list; + lxb_css_selectors_t *selectors = parser->selectors; + + if (failed) { + list = selectors->list_last; + + if (list != NULL) { + lxb_css_selector_list_selectors_remove(selectors, list); + lxb_css_selector_list_destroy(list); + } + } + + return LXB_STATUS_OK; +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_complex(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_complex_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_compound(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_compound_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_simple(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_simple_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_relative(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_relative_cb, + data, length); +} diff --git a/ext/dom/lexbor/lexbor/css/selectors/state.c b/ext/dom/lexbor/lexbor/css/selectors/state.c new file mode 100644 index 0000000000000..873e7d31cfda0 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/selectors/state.c @@ -0,0 +1,2053 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/parser.h" +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_const.h" + + +static const char lxb_css_selectors_module_name[] = "Selectors"; + + +#define lxb_css_selectors_state_string_dup_m(selectors, name) \ + do { \ + (status) = lxb_css_syntax_token_string_dup( \ + lxb_css_syntax_token_string(token), (name), \ + (parser)->memory->mraw); \ + if ((status) != LXB_STATUS_OK) { \ + return (status); \ + } \ + } \ + while (false) + +#define lxb_css_selectors_state_append(parser, selectors, selector) \ + do { \ + (selector) = lxb_css_selector_create((selectors)->list_last); \ + if ((selector) == NULL) { \ + return lxb_css_parser_memory_fail(parser); \ + } \ + \ + lxb_css_selectors_append_next((selectors), (selector)); \ + \ + (selector)->combinator = (selectors)->combinator; \ + (selectors)->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; \ + } \ + while (false) + +#define lxb_css_selectors_state_list_append(parser, selectors, list) \ + do { \ + (list) = lxb_css_selector_list_create((parser)->memory); \ + if ((list) == NULL) { \ + return lxb_css_parser_memory_fail(parser); \ + } \ + \ + lxb_css_selectors_list_append_next((selectors), (list)); \ + \ + (list)->parent = selectors->parent; \ + } \ + while (false) + + +static bool +lxb_css_selectors_state_complex_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_list_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool list, bool root); + +static bool +lxb_css_selectors_state_complex_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_complex_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_complex_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool root); + +static bool +lxb_css_selectors_state_compound_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_sub(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_pseudo(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_selectors_state_hash(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_element_ns(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_attribute(lxb_css_parser_t *parser); + +static lxb_status_t +lxb_css_selectors_state_ns(lxb_css_parser_t *parser, + lxb_css_selector_t *selector); + +static lxb_status_t +lxb_css_selectors_state_ns_ident(lxb_css_parser_t *parser, + lxb_css_selector_t *selector); + +static lxb_status_t +lxb_css_selectors_state_pseudo_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_pseudo_class_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success); + +static lxb_status_t +lxb_css_selectors_state_pseudo_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_pseudo_element_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success); + +static lxb_status_t +lxb_css_selectors_state_forgiving_cb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, lxb_css_parser_state_f state, + bool failed); + +static void +lxb_css_selectors_state_restore_parent(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *last); + +static bool +lxb_css_selectors_state_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state); + +static bool +lxb_css_selectors_state_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static const lxb_css_syntax_token_t * +lxb_css_selectors_state_function_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + + +lxb_inline bool +lxb_css_selectors_done(lxb_css_parser_t *parser) +{ + lxb_css_parser_states_pop(parser); + + return lxb_css_parser_states_set_back(parser); +} + +lxb_inline void +lxb_css_selectors_state_specificity_set_b(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_b(last->specificity, + lxb_css_selector_sp_b(last->specificity) + 1); + } + else if (last->specificity > LXB_CSS_SELECTOR_SP_B_MAX) { + if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + lxb_css_selector_sp_set_b(last->specificity, 1); + } + } + else { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_b(last->specificity, 1); + } +} + +lxb_inline void +lxb_css_selectors_state_specificity_set_c(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_c(last->specificity, + lxb_css_selector_sp_c(last->specificity) + 1); + } + else if (last->specificity > LXB_CSS_SELECTOR_SP_C_MAX) { + if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + lxb_css_selector_sp_set_c(last->specificity, 1); + } + } + else { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_c(last->specificity, 1); + } +} + +lxb_inline void +lxb_css_selectors_state_func_specificity(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *prev, *last; + + last = selectors->list_last; + prev = last->prev; + + if (prev->specificity > last->specificity) { + last->specificity = prev->specificity; + } + + prev->specificity = 0; +} + +/* + * + */ +bool +lxb_css_selectors_state_complex_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_complex_wo_root, + lxb_css_selectors_state_complex_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_complex_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_relative_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_relative_list_wo_root, + lxb_css_selectors_state_relative_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_relative_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_relative_list_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_relative_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_relative_list_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_relative_handler(parser, token, ctx, true, + false); +} + +static bool +lxb_css_selectors_state_relative_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_relative_handler(parser, token, ctx, false, + false); +} + +static bool +lxb_css_selectors_state_relative_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool list, bool root) +{ + lxb_css_parser_state_f back; + lxb_css_parser_state_t *states; + lxb_css_selectors_t *selectors = parser->selectors; + + /* */ + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + return true; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '>': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CHILD; + break; + + case '+': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_SIBLING; + break; + + case '~': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING; + break; + + case '|': + lxb_css_parser_token_next_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CELL; + break; + } + + goto done; + + default: + goto done; + } + + break; + + default: + goto done; + } + + lxb_css_syntax_parser_consume(parser); + +done: + + back = (list) ? lxb_css_selectors_state_complex_end + : lxb_css_selectors_state_end; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + back, ctx, root); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return true; +} + +/* + * + */ +bool +lxb_css_selectors_state_complex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_complex_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_complex_handler(parser, token, ctx, false); +} + +static bool +lxb_css_selectors_state_complex_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool root) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_complex_end, + ctx, root); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selectors_t *selectors = parser->selectors; + + /* */ + +again: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + return lxb_css_selectors_done(parser); + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '>': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CHILD; + break; + + case '+': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_SIBLING; + break; + + case '~': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING; + break; + + case '|': + lxb_css_parser_token_next_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CELL; + break; + } + + goto done; + + default: + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT) { + goto unexpected; + } + + goto done; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return lxb_css_selectors_done(parser); + + default: + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT) { + goto unexpected; + } + + goto done; + } + + lxb_css_syntax_parser_consume(parser); + +done: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_handler); + + return true; + +unexpected: + + (void) lxb_css_selectors_done(parser); + + return lxb_css_parser_unexpected(parser); +} + +/* + * + */ +bool +lxb_css_selectors_state_compound_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_compound_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_compound_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_compound_wo_root); +} + +/* + * + * + */ +bool +lxb_css_selectors_state_compound(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_compound_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selector_list_t *list; + + lxb_css_selectors_state_list_append(parser, parser->selectors, list); + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_handler); + + return false; +} + +static bool +lxb_css_selectors_state_compound_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + lxb_css_selectors_t *selectors; + +again: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_sub); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + case '|': + case '*': + status = lxb_css_selectors_state_element_ns(parser, token); + break; + + default: + goto unexpected; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + status = lxb_css_selectors_state_element(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_compound_pseudo); + status = lxb_css_selectors_state_pseudo_element(parser, token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_element_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + goto unexpected; + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_sub); + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + selectors = parser->selectors; + + if (selectors->combinator > LXB_CSS_SELECTOR_COMBINATOR_CLOSE + || selectors->list_last->first == NULL) + { + goto unexpected; + } + + return lxb_css_selectors_done(parser); + + default: + goto unexpected; + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + +unexpected: + + (void) lxb_css_parser_states_to_root(parser); + (void) lxb_css_parser_states_set_back(parser); + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_compound_sub(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + + /* */ + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + default: + return lxb_css_parser_states_set_back(parser); + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_compound_pseudo); + status = lxb_css_selectors_state_pseudo_element(parser, + token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_element_function(parser, + token, lxb_css_selectors_state_compound_pseudo); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_sub); + break; + + default: + return lxb_css_parser_states_set_back(parser); + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_compound_pseudo(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COLON) { + return lxb_css_parser_states_set_back(parser); + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_element(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + status = lxb_css_selectors_state_pseudo_element_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + } + else { + return lxb_css_parser_unexpected(parser); + } + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + else { + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + + return lxb_css_parser_unexpected(parser); +} + +/* + * + */ +bool +lxb_css_selectors_state_simple_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, lxb_css_selectors_state_simple_wo_root, + lxb_css_selectors_state_simple_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_simple_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_simple_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_simple(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_simple_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_simple_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selector_list_t *list; + + lxb_css_selectors_state_list_append(parser, parser->selectors, list); + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_simple_handler); + + return false; +} + +static bool +lxb_css_selectors_state_simple_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + +again: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_simple_back); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + case '|': + case '*': + status = lxb_css_selectors_state_element_ns(parser, token); + break; + + default: + goto unexpected; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + status = lxb_css_selectors_state_element(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + goto unexpected; + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_simple_back); + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + return lxb_css_parser_states_set_back(parser); + + default: + goto unexpected; + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + +unexpected: + + (void) lxb_css_parser_states_set_back(parser); + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_simple_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_parser_states_set_back(parser); +} + +static lxb_status_t +lxb_css_selectors_state_hash(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *last; + + selectors = parser->selectors; + last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_a(last->specificity, + lxb_css_selector_sp_a(last->specificity) + 1); + } + else if (lxb_css_selector_sp_a(last->specificity) == 0) { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_a(last->specificity, 1); + } + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ID; + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + lxb_css_syntax_parser_consume(parser); + + return status; +} + +static lxb_status_t +lxb_css_selectors_state_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_b(selectors); + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_CLASS; + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + lxb_css_syntax_parser_consume(parser); + + return status; + } + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_element_ns(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ANY; + + selector->name.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->name.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->name.data[0] = '*'; + selector->name.data[1] = '\0'; + selector->name.length = 1; + + if (lxb_css_syntax_token_delim_char(token) == '*') { + lxb_css_syntax_parser_consume(parser); + return lxb_css_selectors_state_ns(parser, selector); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_selectors_state_ns_ident(parser, selector); +} + +static lxb_status_t +lxb_css_selectors_state_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_c(selectors); + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ELEMENT; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_selectors_state_ns(parser, selector); +} + + +static lxb_status_t +lxb_css_selectors_state_attribute(lxb_css_parser_t *parser) +{ + lxb_char_t modifier; + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_syntax_token_t *token; + lxb_css_selector_attribute_t *attribute; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim_char(token) != '|') { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + goto failed; + } + + selector->type = LXB_CSS_SELECTOR_TYPE_ATTRIBUTE; + + selector->ns.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->ns.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->ns.data[0] = '*'; + selector->ns.data[1] = '\0'; + selector->ns.length = 1; + + lxb_css_selectors_state_string_dup_m(parser->selectors, + &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + selector->type = LXB_CSS_SELECTOR_TYPE_ATTRIBUTE; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_DELIM + || lxb_css_syntax_token_delim_char(token) != '|') + { + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + break; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + attribute = &selector->u.attribute; + attribute->match = LXB_CSS_SELECTOR_MATCH_DASH; + + goto assignment; + } + + selector->ns = selector->name; + lexbor_str_clean_all(&selector->name); + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + break; + + default: + goto failed; + } + + attribute = &selector->u.attribute; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + goto done; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '~': + attribute->match = LXB_CSS_SELECTOR_MATCH_INCLUDE; + break; + + case '|': + attribute->match = LXB_CSS_SELECTOR_MATCH_DASH; + break; + + case '^': + attribute->match = LXB_CSS_SELECTOR_MATCH_PREFIX; + break; + + case '$': + attribute->match = LXB_CSS_SELECTOR_MATCH_SUFFIX; + break; + + case '*': + attribute->match = LXB_CSS_SELECTOR_MATCH_SUBSTRING; + break; + + case '=': + attribute->match = LXB_CSS_SELECTOR_MATCH_EQUAL; + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + goto string_or_ident; + + default: + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + break; + + default: + goto failed; + } + +assignment: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_DELIM + || lxb_css_syntax_token_delim_char(token) != '=') + { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + +string_or_ident: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_STRING + && token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) + { + goto failed; + } + + lxb_css_selectors_state_string_dup_m(selectors, &attribute->value); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_RS_BRACKET) { + goto done; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + goto failed; + } + + modifier = *lxb_css_syntax_token_string(token)->data; + + switch (modifier) { + case 'i': + attribute->modifier = LXB_CSS_SELECTOR_MODIFIER_I; + break; + + case 's': + attribute->modifier = LXB_CSS_SELECTOR_MODIFIER_S; + break; + + default: + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_RS_BRACKET) { + goto failed; + } + +done: + + lxb_css_selectors_state_specificity_set_b(selectors); + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +failed: + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_ns(lxb_css_parser_t *parser, + lxb_css_selector_t *selector) +{ + const lxb_css_syntax_token_t *token; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + return lxb_css_selectors_state_ns_ident(parser, selector); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_ns_ident(lxb_css_parser_t *parser, + lxb_css_selector_t *selector) +{ + lxb_status_t status; + const lxb_css_syntax_token_t *token; + lxb_css_selectors_t *selectors; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_c(selectors); + + selector->type = LXB_CSS_SELECTOR_TYPE_ELEMENT; + + selector->ns = selector->name; + lexbor_str_clean_all(&selector->name); + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + + lxb_css_syntax_parser_consume(parser); + + return status; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '*') + { + lxb_css_syntax_parser_consume(parser); + + selector->type = LXB_CSS_SELECTOR_TYPE_ANY; + + selector->ns = selector->name; + + selector->name.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->name.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->name.data[0] = '*'; + selector->name.data[1] = '\0'; + selector->name.length = 1; + + return LXB_STATUS_OK; + } + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_log_message_t *msg; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_selectors_pseudo_data_t *pseudo; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + pseudo = lxb_css_selector_pseudo_class_by_name(selector->name.data, + selector->name.length); + if (pseudo == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (pseudo->id) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING: + msg = lxb_css_log_not_supported(parser->log, + lxb_css_selectors_module_name, + (const char *) selector->name.data); + if (msg == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return lxb_css_parser_unexpected_status(parser); + + default: + break; + } + + selector->u.pseudo.type = pseudo->id; + selector->u.pseudo.data = NULL; + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_class_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_log_message_t *msg; + lxb_css_syntax_rule_t *rule; + const lxb_css_selectors_pseudo_data_func_t *func; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + func = lxb_css_selector_pseudo_class_function_by_name(selector->name.data, + selector->name.length); + if (func == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (func->id) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + msg = lxb_css_log_not_supported(parser->log, + lxb_css_selectors_module_name, + (const char *) selector->name.data); + if (msg == NULL) { + goto failed; + } + + return lxb_css_parser_unexpected_status(parser); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + lxb_css_selectors_state_specificity_set_b(selectors); + break; + + default: + break; + } + + selector->u.pseudo.type = func->id; + selector->u.pseudo.data = NULL; + + selectors->combinator = func->combinator; + selectors->comb_default = func->combinator; + selectors->parent = selector; + + rule = lxb_css_syntax_parser_function_push(parser, token, success, + &func->cb, selectors->list_last); + if (rule == NULL) { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +failed: + + (void) lxb_css_parser_memory_fail(parser); + + return parser->status; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_log_message_t *msg; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_selectors_pseudo_data_t *pseudo; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + pseudo = lxb_css_selector_pseudo_element_by_name(selector->name.data, + selector->name.length); + if (pseudo == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (pseudo->id) { + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT: + msg = lxb_css_log_not_supported(parser->log, + lxb_css_selectors_module_name, + (const char *) selector->name.data); + if (msg == NULL) { + (void) lxb_css_parser_memory_fail(parser); + return parser->status; + } + + return lxb_css_parser_unexpected_status(parser); + + default: + break; + } + + selector->u.pseudo.type = pseudo->id; + selector->u.pseudo.data = NULL; + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_element_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_syntax_rule_t *rule; + const lxb_css_selectors_pseudo_data_func_t *func; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + func = lxb_css_selector_pseudo_element_function_by_name(selector->name.data, + selector->name.length); + if (func == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + selector->u.pseudo.type = func->id; + selector->u.pseudo.data = NULL; + + selectors->combinator = func->combinator; + selectors->comb_default = func->combinator; + selectors->parent = selector; + + rule = lxb_css_syntax_parser_function_push(parser, token, success, + &func->cb, selectors->list_last); + if (rule == NULL) { + (void) lxb_css_parser_memory_fail(parser); + return parser->status; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +lxb_inline void +lxb_css_selectors_state_restore_combinator(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_t *parent; + lxb_css_selector_combinator_t comb_default; + const lxb_css_selectors_pseudo_data_func_t *data_func; + + comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + + if (selectors->parent != NULL) { + parent = selectors->parent; + + if (parent->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION) { + data_func = lxb_css_selector_pseudo_class_function_by_id(parent->u.pseudo.type); + } + else { + data_func = lxb_css_selector_pseudo_element_function_by_id(parent->u.pseudo.type); + } + + comb_default = data_func->combinator; + } + + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + selectors->comb_default = comb_default; +} + +lxb_status_t +lxb_css_selectors_state_function_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + bool cy; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + + if (token->type == LXB_CSS_SYNTAX_TOKEN__EOF) { + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_ERROR, + "%s. End Of File in pseudo function", + lxb_css_selectors_module_name); + } + + if (selectors->list_last == NULL) { + lxb_css_selectors_state_restore_parent(selectors, ctx); + goto empty; + } + + lxb_css_selectors_state_restore_parent(selectors, ctx); + + return LXB_STATUS_OK; + +empty: + + selector = selectors->list_last->last; + + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + cy = lxb_css_selector_pseudo_function_can_empty(selector->u.pseudo.type, + cy); + if (cy) { + lxb_css_parser_set_ok(parser); + return LXB_STATUS_OK; + } + + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_ERROR, + "%s. Pseudo function can't be empty: %S()", + lxb_css_selectors_module_name, &selector->name); + + lxb_css_selector_remove(selector); + lxb_css_selector_destroy(selector); + + lxb_css_parser_failed_set_by_id(parser, -1, true); + selectors->err_in_function = true; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_selectors_state_function_forgiving(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return lxb_css_selectors_state_forgiving_cb(parser, token, ctx, + lxb_css_selectors_state_complex_list, + failed); +} + +lxb_status_t +lxb_css_selectors_state_function_forgiving_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return lxb_css_selectors_state_forgiving_cb(parser, token, ctx, + lxb_css_selectors_state_relative_list, + failed); +} + +static lxb_status_t +lxb_css_selectors_state_forgiving_cb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, lxb_css_parser_state_f state, + bool failed) +{ + bool cy; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + + lxb_css_parser_set_ok(parser); + + if (token->type == LXB_CSS_SYNTAX_TOKEN__EOF) { + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_ERROR, + "%s. End Of File in pseudo function", + lxb_css_selectors_module_name); + } + + if (selectors->list_last == NULL) { + lxb_css_selectors_state_restore_parent(selectors, ctx); + goto empty; + } + + if (selectors->parent->u.pseudo.type + == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE) + { + selectors->list_last->specificity = 0; + } + + lxb_css_selectors_state_restore_parent(selectors, ctx); + + return LXB_STATUS_OK; + +empty: + + selector = selectors->list_last->last; + + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + cy = lxb_css_selector_pseudo_function_can_empty(selector->u.pseudo.type, + cy); + if (cy) { + return LXB_STATUS_OK; + } + + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_ERROR, + "%s. Pseudo function can't be empty: %S()", + lxb_css_selectors_module_name, &selector->name); + + lxb_css_selector_remove(selector); + lxb_css_selector_destroy(selector); + + lxb_css_parser_failed_set_by_id(parser, -1, true); + selectors->err_in_function = true; + + return LXB_STATUS_OK; +} + +static void +lxb_css_selectors_state_restore_parent(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *last) +{ + uint32_t src, dst; + + if (selectors->list_last != NULL && selectors->list_last != last) { + dst = last->specificity; + src = selectors->list_last->specificity; + + selectors->list_last = 0; + + if (last->parent == NULL) { + lxb_css_selector_sp_add_a(dst, lxb_css_selector_sp_a(src)); + lxb_css_selector_sp_add_b(dst, lxb_css_selector_sp_b(src)); + lxb_css_selector_sp_add_c(dst, lxb_css_selector_sp_c(src)); + } + else if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + dst |= src; + } + else if (src > dst) { + dst = src; + } + + last->specificity = dst; + } + + if (selectors->list != NULL) { + last->last->u.pseudo.data = selectors->list; + } + + selectors->list_last = last; + + /* Get first Selector in chain. */ + while (last->prev != NULL) { + last = last->prev; + } + + selectors->list = last; + selectors->parent = last->parent; + + lxb_css_selectors_state_restore_combinator(selectors); +} + +static bool +lxb_css_selectors_state_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state) +{ + lxb_css_parser_state_t *states; + lxb_css_selectors_t *selectors = parser->selectors; + + if (lxb_css_parser_is_failed(parser)) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + if (selectors->parent != NULL && selectors->list_last && + selectors->list_last->prev != NULL) + { + lxb_css_selectors_state_func_specificity(selectors); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + states = lxb_css_parser_states_current(parser); + + if (states->root) { + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + (void) lxb_css_parser_states_pop(parser); + return lxb_css_parser_success(parser); + } + + return lxb_css_selectors_done(parser); + } + + selectors->combinator = selectors->comb_default; + + lxb_css_syntax_token_consume(parser->tkz); + lxb_css_parser_state_set(parser, state); + lxb_css_parser_set_ok(parser); + + return true; +} + +static bool +lxb_css_selectors_state_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + if (lxb_css_parser_is_failed(parser)) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + states = lxb_css_parser_states_current(parser); + + if (states->root) { + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + (void) lxb_css_parser_states_pop(parser); + return lxb_css_parser_success(parser); + } + + return lxb_css_selectors_done(parser); +} + + +static const lxb_css_syntax_token_t * +lxb_css_selectors_state_function_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + bool cy, comma; + lxb_css_selector_list_t *list; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + const lxb_css_syntax_token_t *origin; + const lxb_css_selectors_pseudo_data_func_t *func; + + cy = false; + comma = true; + list = selectors->list_last; + selector = selectors->parent; + + if (selector != NULL) { + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + + func = lxb_css_selector_pseudo_function_by_id(selector->u.pseudo.type, + cy); + if (func == NULL) { + return NULL; + } + + cy = func->forgiving; + comma = func->comma; + } + + if (!selectors->err_in_function) { + origin = lxb_css_syntax_token(parser->tkz); + if (origin == NULL) { + return NULL; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + origin = token; + } + else if (origin->type != LXB_CSS_SYNTAX_TOKEN__EOF) { + origin = NULL; + } + + if (origin != NULL) { + if (lxb_css_syntax_token_error(parser, origin, + "Selectors") == NULL) + { + return NULL; + } + } + } + + selectors->err_in_function = false; + + if (cy) { + lxb_css_selector_list_selectors_remove(selectors, list); + lxb_css_selector_list_destroy(list); + + while (token != NULL + && token->type != LXB_CSS_SYNTAX_TOKEN__END) + { + if (comma == true + && token->type == LXB_CSS_SYNTAX_TOKEN_COMMA + && lxb_css_parser_rule_deep(parser) == 0) + { + break; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return token; + } + + lxb_css_selector_list_destroy_chain(selectors->list); + + selectors->list = NULL; + selectors->list_last = NULL; + + while (token != NULL + && token->type != LXB_CSS_SYNTAX_TOKEN__END) + { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return token; +} diff --git a/ext/dom/lexbor/lexbor/css/state.c b/ext/dom/lexbor/lexbor/css/state.c new file mode 100644 index 0000000000000..15a9614948f26 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/state.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/state.h" +#include "lexbor/css/css.h" +#include "lexbor/css/at_rule/state.h" + + +bool +lxb_css_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + return true; + + case LXB_CSS_SYNTAX_TOKEN__END: + return true; + + default: + break; + } + + return lxb_css_parser_failed(parser); +} + +bool +lxb_css_state_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + /* The lxb_css_syntax_parser_consume(...) locked in this state. */ + + lxb_css_syntax_token_consume(parser->tkz); + + return true; +} + +bool +lxb_css_state_stop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_stop(parser); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/anb.c b/ext/dom/lexbor/lexbor/css/syntax/anb.c new file mode 100644 index 0000000000000..ffd1751cdabf6 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/anb.c @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2021-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/conv.h" +#include "lexbor/core/serialize.h" +#include "lexbor/css/css.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/syntax/anb.h" + + +static bool +lxb_css_syntax_anb_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_syntax_anb_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static lxb_css_log_message_t * +lxb_css_syntax_anb_fail(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_syntax_anb_state_ident(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb); + +static lxb_status_t +lxb_css_syntax_anb_state_ident_data(lxb_css_parser_t *parser, + lxb_css_syntax_anb_t *anb, + const lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + + +static const lxb_css_syntax_cb_pipe_t lxb_css_syntax_anb_pipe = { + .state = lxb_css_syntax_anb_state, + .block = NULL, + .failed = lxb_css_state_failed, + .end = lxb_css_syntax_anb_end +}; + + +lxb_css_syntax_anb_t +lxb_css_syntax_anb_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + lxb_status_t status; + lxb_css_syntax_anb_t anb; + lxb_css_syntax_rule_t *rule; + + memset(&anb, 0, sizeof(lxb_css_syntax_anb_t)); + + if (parser->stage != LXB_CSS_PARSER_CLEAN) { + if (parser->stage == LXB_CSS_PARSER_RUN) { + parser->status = LXB_STATUS_ERROR_WRONG_ARGS; + return anb; + } + + lxb_css_parser_clean(parser); + } + + lxb_css_parser_buffer_set(parser, data, length); + + rule = lxb_css_syntax_parser_pipe_push(parser, NULL, + &lxb_css_syntax_anb_pipe, &anb, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + return anb; + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + status = lxb_css_syntax_parser_run(parser); + if (status != LXB_STATUS_OK) { + /* Destroy. */ + } + + parser->stage = LXB_CSS_PARSER_END; + + return anb; +} + +static bool +lxb_css_syntax_anb_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + parser->status = lxb_css_syntax_anb_handler(parser, token, ctx); + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + if (parser->status != LXB_STATUS_OK + || (token->type != LXB_CSS_SYNTAX_TOKEN__END)) + { + (void) lxb_css_syntax_anb_fail(parser, token); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_syntax_anb_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static lxb_css_log_message_t * +lxb_css_syntax_anb_fail(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + static const char anb[] = "An+B"; + + return lxb_css_syntax_token_error(parser, token, anb); +} + +lxb_status_t +lxb_css_syntax_anb_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb) +{ + const lxb_char_t *data, *end; + lxb_css_syntax_token_ident_t *ident; + +again: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + if (lxb_css_syntax_token_dimension(token)->num.is_float) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = lexbor_conv_double_to_long(lxb_css_syntax_token_dimension(token)->num.num); + + ident = lxb_css_syntax_token_dimension_string(token); + + goto ident; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + return lxb_css_syntax_anb_state_ident(parser, token, anb); + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (lxb_css_syntax_token_number(token)->is_float) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = 0; + anb->b = lexbor_conv_double_to_long(lxb_css_syntax_token_number(token)->num); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '+') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = 1; + + ident = lxb_css_syntax_token_ident(token); + + goto ident; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + goto again; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +ident: + + data = ident->data; + end = ident->data + ident->length; + + if (*data != 'n' && *data != 'N') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + data++; + + return lxb_css_syntax_anb_state_ident_data(parser, anb, token, data, end); +} + +static lxb_status_t +lxb_css_syntax_anb_state_ident(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb) +{ + size_t length; + lxb_char_t c; + const lxb_char_t *data, *end; + lxb_css_syntax_token_ident_t *ident; + + static const lxb_char_t odd[] = "odd"; + static const lxb_char_t even[] = "even"; + + ident = lxb_css_syntax_token_ident(token); + + length = ident->length; + data = ident->data; + end = data + length; + + c = *data++; + + /* 'n' or '-n' */ + + if (c == 'n' || c == 'N') { + anb->a = 1; + } + else if (c == '-') { + if (data >= end) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + c = *data++; + + if (c != 'n' && c != 'N') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = -1; + } + else if (length == sizeof(odd) - 1 + && lexbor_str_data_ncasecmp(ident->data, odd, sizeof(odd) - 1)) + { + anb->a = 2; + anb->b = 1; + + lxb_css_syntax_parser_consume(parser); + return LXB_STATUS_OK; + } + else if (length == sizeof(even) - 1 + && lexbor_str_data_ncasecmp(ident->data, even, sizeof(even) - 1)) + { + anb->a = 2; + anb->b = 0; + + lxb_css_syntax_parser_consume(parser); + return LXB_STATUS_OK; + } + else { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return lxb_css_syntax_anb_state_ident_data(parser, anb, token, data, end); +} + +static lxb_status_t +lxb_css_syntax_anb_state_ident_data(lxb_css_parser_t *parser, + lxb_css_syntax_anb_t *anb, + const lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + unsigned sign; + lxb_char_t c; + const lxb_char_t *p; + + sign = 0; + + if (data >= end) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (!lxb_css_syntax_token_number(token)->have_sign) { + anb->b = 0; + return LXB_STATUS_OK; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + c = lxb_css_syntax_token_delim(token)->character; + + switch (c) { + case '-': + sign = 1; + break; + + case '+': + sign = 2; + break; + + default: + anb->b = 0; + return LXB_STATUS_OK; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + break; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + anb->b = 0; + return LXB_STATUS_OK; + + default: + anb->b = 0; + return LXB_STATUS_OK; + } + + goto number; + } + + c = *data++; + + if (c != '-') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (data < end) { + p = data; + anb->b = -lexbor_conv_data_to_long(&data, end - data); + + if (anb->b > 0 || data == p || data < end) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + goto done; + } + + sign = 1; + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + +number: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_NUMBER) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (lxb_css_syntax_token_number(token)->is_float + || (sign > 0 && lxb_css_syntax_token_number(token)->have_sign)) + { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->b = lexbor_conv_double_to_long(lxb_css_syntax_token_number(token)->num); + + if (sign == 1) { + anb->b = -anb->b; + } + +done: + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_anb_serialize(lxb_css_syntax_anb_t *anb, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t buf[128]; + lxb_char_t *p, *end; + + if (anb == NULL) { + return LXB_STATUS_OK; + } + + static const lxb_char_t odd[] = "odd"; + static const lxb_char_t even[] = "even"; + + if (anb->a == 2) { + if (anb->b == 1) { + return cb(odd, sizeof(odd) - 1, ctx); + } + + if (anb->b == 0) { + return cb(even, sizeof(even) - 1, ctx); + } + } + + p = buf; + end = p + sizeof(buf); + + if (anb->a == 1) { + *p = '+'; + p++; + } + else if (anb->a == -1) { + *p = '-'; + p++; + } + else { + p += lexbor_conv_float_to_data((double) anb->a, p, end - p); + if (p >= end) { + return LXB_STATUS_ERROR_SMALL_BUFFER; + } + } + + *p = 'n'; + p++; + + if (p >= end) { + return cb(buf, p - buf, ctx); + } + + if (anb->b == 0) { + return cb(buf, p - buf, ctx); + } + + if (anb->b > 0) { + *p = '+'; + p++; + + if (p >= end) { + return LXB_STATUS_ERROR_SMALL_BUFFER; + } + } + + p += lexbor_conv_float_to_data((double) anb->b, p, end - p); + + return cb(buf, p - buf, ctx); +} + +lxb_char_t * +lxb_css_syntax_anb_serialize_char(lxb_css_syntax_anb_t *anb, size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_syntax_anb_serialize(anb, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_syntax_anb_serialize(anb, lexbor_serialize_copy_cb, &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/parser.c b/ext/dom/lexbor/lexbor/css/syntax/parser.c new file mode 100644 index 0000000000000..f7c7728e7cfcb --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/parser.c @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/state.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/at_rule/state.h" + + +static const lxb_css_syntax_token_t lxb_css_syntax_token_terminated = +{ + .types.terminated = {.begin = NULL, .length = 0, .user_id = 0}, + .type = LXB_CSS_SYNTAX_TOKEN__END, + .offset = 0, + .cloned = false +}; + + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static bool +lxb_css_syntax_parser_list_rules_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static bool +lxb_css_syntax_parser_declarations_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_drop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + + +lxb_inline const lxb_css_syntax_token_t * +lxb_css_syntax_parser_failed(lxb_css_parser_t *parser, lxb_status_t status) +{ + parser->status = status; + return NULL; +} + + +lxb_status_t +lxb_css_syntax_parser_run(lxb_css_parser_t *parser) +{ + const lxb_css_syntax_token_t *token; + + parser->loop = true; + + do { + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + if (parser->fake_null) { + parser->fake_null = false; + continue; + } + + return parser->status; + } + + while (parser->rules->state(parser, token, + parser->rules->context) == false) {}; + } + while (parser->loop); + + return parser->status; +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token(lxb_css_parser_t *parser) +{ + lxb_css_syntax_token_t *token; + lxb_css_syntax_rule_t *rule = parser->rules; + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + return rule->phase(parser, token, rule); +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token_wo_ws(lxb_css_parser_t *parser) +{ + const lxb_css_syntax_token_t *token; + + token = lxb_css_syntax_parser_token(parser); + + if (token != NULL && token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + return lxb_css_syntax_parser_token(parser); + } + + return token; +} + +void +lxb_css_syntax_parser_consume(lxb_css_parser_t *parser) +{ + if (!parser->rules->skip_consume) { + lxb_css_syntax_token_consume(parser->tkz); + } +} + +lxb_status_t +lxb_css_syntax_parser_tkz_cb(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t **data, const lxb_char_t **end, + void *ctx) +{ + size_t length, size; + lxb_char_t *new_data; + lxb_status_t status; + lxb_css_parser_t *parser = ctx; + + if (parser->pos == NULL) { + return parser->chunk_cb(tkz, data, end, parser->chunk_ctx); + } + + length = (size_t) (*end - parser->pos); + + if (SIZE_MAX - parser->str.length < length) { + return LXB_STATUS_ERROR_OVERFLOW; + } + + if (parser->str.length + length >= parser->str_size) { + size = parser->str.length + length + 1; + + new_data = lexbor_realloc(parser->str.data, size); + if (new_data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->str.data = new_data; + parser->str_size = size; + } + + memcpy(parser->str.data + parser->str.length, parser->pos, length); + + status = parser->chunk_cb(tkz, data, end, parser->chunk_ctx); + parser->str.length += length; + parser->pos = *data; + + return status; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_list_rules_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_list_rules_t *cb_rules, + void *ctx, bool top_level, + lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_list_rules; + rule->state = cb_rules->cb.state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_list_rules; + rule->cbx.list_rules = cb_rules; + rule->context = ctx; + rule->block_end = stop; + rule->top_level = top_level; + + if (token != NULL) { + rule->u.list_rules.begin = token->offset; + } + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_at_rule_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_at_rule_t *at_rule, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_at_rule_offset_t *at; + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_at_rule; + rule->state = at_rule->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_at_rule; + rule->cbx.at_rule = at_rule; + rule->context = ctx; + rule->block_end = stop; + + if (token != NULL) { + at = &rule->u.at_rule; + + at->name = token->offset; + at->prelude = token->offset + lxb_css_syntax_token_base(token)->length; + } + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_qualified_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_qualified_rule_t *qualified, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_qualified_rule; + rule->state = qualified->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_qualified_rule; + rule->cbx.qualified_rule = qualified; + rule->context = ctx; + rule->block_end = stop; + + if (token != NULL) { + rule->u.qualified.prelude = token->offset; + } + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_declarations_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_declarations_t *declarations, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_declarations; + rule->state = declarations->cb.state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_declarations; + rule->cbx.declarations = declarations; + rule->context = ctx; + rule->block_end = stop; + + if (token != NULL) { + rule->u.declarations.begin = token->offset; + } + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_components_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_components_t *comp, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_components; + rule->state = comp->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_components; + rule->cbx.components = comp; + rule->context = ctx; + rule->block_end = stop; + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_function_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_function_t *func, + void *ctx) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + if (token == NULL || token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + parser->status = LXB_STATUS_ERROR_WRONG_ARGS; + return NULL; + } + + if (parser->rules > parser->rules_begin) { + rule = parser->rules; + + if (rule->deep != 0 + && parser->types_pos[-1] == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) + { + rule->deep--; + parser->types_pos--; + } + } + + parser->rules->state = lxb_css_state_success; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_function; + rule->state = func->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_function; + rule->cbx.func = func; + rule->context = ctx; + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_block_push(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_block_t *block, + void *ctx) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + lxb_css_syntax_token_type_t block_end; + + if (token == NULL) { + parser->status = LXB_STATUS_ERROR_WRONG_ARGS; + return NULL; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + block_end = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + block_end = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + block_end = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + break; + + default: + parser->status = LXB_STATUS_ERROR_WRONG_ARGS; + return NULL; + } + + if (parser->rules > parser->rules_begin) { + rule = parser->rules; + + if (rule->deep != 0 && parser->types_pos[-1] == block_end) { + rule->deep--; + parser->types_pos--; + } + } + + parser->rules->state = lxb_css_state_success; + + lxb_css_parser_offset_set(parser, token); + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_block; + rule->state = block->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_block; + rule->cbx.block = block; + rule->context = ctx; + rule->block_end = block_end; + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_pipe_push(lxb_css_parser_t *parser, + lxb_css_parser_state_f state_back, + const lxb_css_syntax_cb_pipe_t *pipe, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + status = lxb_css_syntax_stack_expand(parser, 1); + if (status != LXB_STATUS_OK) { + parser->status = status; + return NULL; + } + + parser->rules->state = lxb_css_state_success; + + rule = ++parser->rules; + + memset(rule, 0x00, sizeof(lxb_css_syntax_rule_t)); + + rule->phase = lxb_css_syntax_parser_pipe; + rule->state = pipe->state; + rule->state_back = state_back; + rule->back = lxb_css_syntax_parser_pipe; + rule->cbx.pipe = pipe; + rule->context = ctx; + rule->block_end = stop; + + parser->context = NULL; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_stack_pop(lxb_css_parser_t *parser) +{ + return parser->rules--; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->offset > token->offset) { + return token; + } + +begin: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + rule->phase = lxb_css_syntax_parser_list_rules_at; + break; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + case LXB_CSS_SYNTAX_TOKEN_CDC: + case LXB_CSS_SYNTAX_TOKEN_CDO: + if (rule->top_level) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + } + + /* fall through */ + + default: + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + rule->phase = lxb_css_syntax_parser_list_rules_qualified; + break; + } + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + rule->u.list_rules.end = token->offset; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->state != lxb_css_state_success) { + return token; + } + + rule = lxb_css_syntax_parser_at_rule_push(parser, token, + lxb_css_syntax_parser_list_rules_back, + rule->cbx.list_rules->at_rule, + rule->context, rule->block_end); + if (rule == NULL) { + return NULL; + } + + parser->fake_null = true; + + return NULL; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->state != lxb_css_state_success) { + return token; + } + + rule = lxb_css_syntax_parser_qualified_push(parser, token, + lxb_css_syntax_parser_list_rules_back, + rule->cbx.list_rules->qualified_rule, + rule->context, rule->block_end); + if (rule == NULL) { + return NULL; + } + + parser->fake_null = true; + + return NULL; +} + +static bool +lxb_css_syntax_parser_list_rules_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_syntax_rule_t *rule; + + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + rule = parser->rules; + rule->state = rule->cbx.list_rules->next; + + return false; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_start_block; + + rule->u.at_rule.prelude_end = token->offset; + rule->u.at_rule.block = token->offset + + lxb_css_syntax_token_base(token)->length; + + rule->skip_consume = true; + + parser->block = rule->cbx.cb->block; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + token = &lxb_css_syntax_token_terminated; + } + + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + if (rule->deep == 1) { + goto done; + } + + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + goto done; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + if (rule->u.at_rule.prelude_end != 0) { + rule->u.at_rule.block_end = token->offset; + } + else { + rule->u.at_rule.prelude_end = token->offset; + } + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + /* It is necessary to avoid re-entry of the token into the phase. */ + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_start_block; + + rule->u.qualified.prelude_end = token->offset; + rule->u.qualified.block = token->offset + + lxb_css_syntax_token_base(token)->length; + + rule->skip_consume = true; + + parser->block = rule->cbx.cb->block; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + token = &lxb_css_syntax_token_terminated; + } + + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + if (rule->deep == 1) { + goto done; + } + + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + if (rule->u.qualified.block != 0) { + rule->u.qualified.block_end = token->offset; + } + else { + rule->u.qualified.prelude_end = token->offset; + } + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->offset > token->offset) { + return token; + } + +begin: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + rule->u.declarations.name_begin = token->offset; + + if (lxb_css_syntax_tokenizer_lookup_colon(parser->tkz)) { + rule->phase = lxb_css_syntax_parser_declarations_name; + parser->block = rule->cbx.cb->block; + + return token; + } + + rule->state = rule->cbx.cb->failed; + rule->phase = lxb_css_syntax_parser_declarations_drop; + rule->failed = true; + + break; + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + rule->u.declarations.name_begin = 0; + + rule = lxb_css_syntax_parser_at_rule_push(parser, token, + lxb_css_syntax_parser_declarations_back, + rule->cbx.declarations->at_rule, rule->context, + rule->block_end); + if (rule != NULL) { + parser->fake_null = true; + } + + return NULL; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + rule->state = rule->cbx.cb->failed; + rule->phase = lxb_css_syntax_parser_declarations_drop; + rule->failed = true; + + rule->u.declarations.name_begin = token->offset; + break; + } + + parser->fake_null = true; + + return NULL; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->state = lxb_css_state_success; + rule->skip_consume = true; + + rule->u.declarations.name_begin = 0; + rule->u.declarations.end = token->offset; + + parser->fake_null = true; + + return NULL; +} + +static bool +lxb_css_syntax_parser_declarations_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_syntax_rule_t *rules = parser->rules; + + rules->state = rules->cbx.declarations->cb.state; + + return rules->state(parser, token, ctx); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->offset > token->offset) { + return token; + } + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + rule->skip_consume = false; + + /* 1. */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + } + + /* 2. */ + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COLON) { + /* Parse error. */ + + /* + * It can't be. + * + * Before entering the lxb_css_syntax_parser_declarations_name() + * function, data validation takes place. In fact, these checks are not + * needed here. + */ + + /* + * But it's good for validation, if we come here it means we're + * doing badly. + */ + + return NULL; + } + + rule->u.declarations.name_end = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + /* 3. */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + } + + rule->u.declarations.value_begin = token->offset; + + /* 4. */ + + rule->phase = lxb_css_syntax_parser_declarations_value; + rule->state = parser->block; + + return lxb_css_syntax_parser_declarations_value(parser, token, rule); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + bool imp; + uintptr_t before_important; + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + +again: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + if (rule->deep != 0) { + return token; + } + + imp = lxb_css_syntax_tokenizer_lookup_declaration_ws_end(parser->tkz, + rule->block_end, + (rule->block_end == LXB_CSS_SYNTAX_TOKEN_RC_BRACKET) ? 0x7D : 0x00); + + if (!imp) { + return token; + } + + before_important = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + /* Have !important? */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + rule->important = true; + rule->u.declarations.before_important = before_important; + + lxb_css_syntax_token_consume(parser->tkz); + + /* Skip important */ + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + } + + goto again; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_declarations_next; + + rule->u.declarations.value_end = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + return &lxb_css_syntax_token_terminated; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '!') { + return token; + } + + imp = lxb_css_syntax_tokenizer_lookup_important(parser->tkz, + rule->block_end, + (rule->block_end == LXB_CSS_SYNTAX_TOKEN_RC_BRACKET) ? 0x7D : 0x00); + + if (!imp) { + return token; + } + + rule->u.declarations.before_important = token->offset; + rule->important = true; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + + goto again; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_declarations_end; + rule->skip_consume = true; + + rule->u.declarations.value_end = token->offset; + rule->u.declarations.end = token->offset; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + lxb_css_syntax_declarations_offset_t *decl; + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + status = rule->cbx.declarations->declaration_end(parser, rule->context, + rule->important, + rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->phase = lxb_css_syntax_parser_declarations; + rule->state = rule->cbx.cb->state; + + rule->skip_consume = false; + rule->important = false; + rule->failed = false; + + decl = &rule->u.declarations; + + decl->name_begin = 0; + decl->name_end = 0; + decl->value_begin = 0; + decl->before_important = 0; + decl->value_end = 0; + + return lxb_css_syntax_parser_declarations(parser, token, rule); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_drop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + /* It is necessary to avoid re-entry of the token into the phase. */ + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_declarations_next; + + rule->u.declarations.name_end = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_declarations_end; + rule->skip_consume = true; + + rule->u.declarations.name_end = token->offset; + rule->u.declarations.end = token->offset; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rules; + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + status = rule->cbx.declarations->declaration_end(parser, rule->context, + rule->important, + rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + status = rule->cbx.cb->end(parser, token, rule->context, false); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + if (!rule->skip_ending) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + + (void) lxb_css_syntax_parser_stack_pop(parser); + + rules = parser->rules; + + if (parser->rules <= parser->rules_begin) { + rules->state = lxb_css_state_stop; + return token; + } + + rules->phase = rules->back; + + return rules->phase(parser, token, rules); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->skip_ending = true; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0) { + if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + } + else { + goto done; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if ((rule->block_end == token->type && rule->deep == 0) + || token->type == LXB_CSS_SYNTAX_TOKEN__EOF) + { + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + return token; +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_start_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + rule->skip_consume = false; + + rule->phase = rule->back; + rule->state = parser->block; + + return rule->back(parser, token, rule); +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rules; + lxb_css_syntax_cb_base_t *base; + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return &lxb_css_syntax_token_terminated; + } + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + base = rule->cbx.user; + + status = base->end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + if (!rule->skip_ending) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + + (void) lxb_css_syntax_parser_stack_pop(parser); + + rules = parser->rules; + + if (parser->rules <= parser->rules_begin) { + rules->state = lxb_css_state_stop; + return token; + } + + rules->phase = rules->back; + rules->state = rule->state_back; + + return rules->phase(parser, token, rules); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/state.c b/ext/dom/lexbor/lexbor/css/syntax/state.c new file mode 100644 index 0000000000000..942ff05012657 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/state.c @@ -0,0 +1,2744 @@ +/* + * Copyright (C) 2018-2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include +#include + +#include "lexbor/core/utils.h" +#include "lexbor/core/strtod.h" + +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/syntax/tokenizer/error.h" + +#define LXB_CSS_SYNTAX_RES_NAME_MAP +#include "lexbor/css/syntax/res.h" + +#define LEXBOR_STR_RES_MAP_HEX +#define LEXBOR_STR_RES_ANSI_REPLACEMENT_CHARACTER +#include "lexbor/core/str_res.h" + + +#define LXB_CSS_SYNTAX_NEXT_CHUNK(_tkz, _status, _data, _end) \ + do { \ + _status = lxb_css_syntax_tokenizer_next_chunk(_tkz, &_data, &_end); \ + if (_status != LXB_STATUS_OK) { \ + return NULL; \ + } \ + } \ + while (0) + + +#define LXB_CSS_SYNTAX_STR_APPEND_LEN(_tkz, _status, _begin, _length) \ + do { \ + _status = lxb_css_syntax_string_append(_tkz, _begin, _length); \ + if (_status != LXB_STATUS_OK) { \ + return NULL; \ + } \ + } \ + while (0) + +#define LXB_CSS_SYNTAX_STR_APPEND(_tkz, _status, _begin, _end) \ + LXB_CSS_SYNTAX_STR_APPEND_LEN(_tkz, _status, _begin, (_end - _begin)) + +#define LXB_CSS_SYNTAX_DELIM_APPEND(_tkz, _begin, _length, _ch) \ + do { \ + if (lxb_css_syntax_list_append_delim(_tkz, _begin, _length, _ch) \ + == NULL) \ + { \ + return NULL; \ + } \ + } \ + while (false) + + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + lxb_char_t *buf_start, lxb_char_t *buf_end, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric_name_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_consume_ident(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_ident_like(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + + +static const lxb_char_t * +lxb_css_syntax_state_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_bad_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_escaped(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, + const lxb_char_t **end, size_t *length); + +static const lxb_char_t * +lxb_css_syntax_state_escaped_string(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, + const lxb_char_t **end, size_t *length); + + +lxb_inline lxb_status_t +lxb_css_syntax_string_realloc(lxb_css_syntax_tokenizer_t *tkz, size_t upto) +{ + size_t len = tkz->pos - tkz->start; + size_t size = (tkz->end - tkz->start) + upto; + + lxb_char_t *tmp = lexbor_realloc(tkz->start, size); + if (tmp == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return tkz->status; + } + + tkz->start = tmp; + tkz->pos = tmp + len; + tkz->end = tmp + size; + + return LXB_STATUS_OK; +} + +lxb_inline lxb_status_t +lxb_css_syntax_string_append(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, size_t length) +{ + if ((size_t) (tkz->end - tkz->pos) <= length) { + if (lxb_css_syntax_string_realloc(tkz, length + 1024) != LXB_STATUS_OK) { + return tkz->status; + } + } + + memcpy(tkz->pos, data, length); + + tkz->pos += length; + + return LXB_STATUS_OK; +} + +lxb_inline lxb_status_t +lxb_css_syntax_state_string_term(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz->pos >= tkz->end) { + if (lxb_css_syntax_string_realloc(tkz, 1024) != LXB_STATUS_OK) { + return tkz->status; + } + } + + *tkz->pos = 0x00; + + return LXB_STATUS_OK; +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_state_string_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data) +{ + if(lxb_css_syntax_state_string_term(tkz) != LXB_STATUS_OK) { + return NULL; + } + + lxb_css_syntax_token_string(token)->data = tkz->start; + lxb_css_syntax_token_string(token)->length = tkz->pos - tkz->start; + + tkz->pos = tkz->start; + + return data; +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_state_dimension_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data) +{ + if(lxb_css_syntax_state_string_term(tkz) != LXB_STATUS_OK) { + return NULL; + } + + lxb_css_syntax_token_dimension_string(token)->data = tkz->start; + lxb_css_syntax_token_dimension_string(token)->length = tkz->pos - tkz->start; + + tkz->pos = tkz->start; + + return data; +} + +lxb_inline lxb_css_syntax_token_t * +lxb_css_syntax_state_token_create(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz->prepared == 0) { + tkz->prepared = tkz->cache->length; + } + + return lxb_css_syntax_token_cached_create(tkz); +} + +/* + * Delim + */ +lxb_inline void +lxb_css_syntax_state_delim_set(lxb_css_syntax_token_t *token, + const lxb_char_t *data, lxb_char_t ch, + size_t length) +{ + lxb_css_syntax_token_delim(token)->character = ch; + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = length; + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; +} + +lxb_inline lxb_css_syntax_token_t * +lxb_css_syntax_list_append_delim(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, + size_t length, lxb_char_t ch) +{ + lxb_css_syntax_token_t *delim; + + delim = lxb_css_syntax_state_token_create(tkz); + if (delim == NULL) { + return NULL; + } + + lxb_css_syntax_state_delim_set(delim, data, ch, length); + + return delim; +} + +const lxb_char_t * +lxb_css_syntax_state_delim(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_css_syntax_state_delim_set(token, data, *data, 1); + + return data + 1; +} + +/* + * Comment + */ +const lxb_char_t * +lxb_css_syntax_state_comment(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *begin; + + lxb_css_syntax_token_base(token)->begin = data; + + /* Skip forward slash (/) */ + data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + /* U+002A ASTERISK (*) */ + if (*data != 0x2A) { + goto delim; + } + + begin = ++data; + length = 2; + + do { + if (data >= end) { + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto error; + } + + begin = data; + } + + switch (*data) { + case 0x00: + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); + data += 1; + length += data - begin; + begin = data; + + continue; + + case 0x0D: + data++; + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + + tkz->pos[-1] = '\n'; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto error; + } + } + + if (*data != 0x0A) { + data--; + } + else { + length += 1; + } + + begin = ++data; + + continue; + + case 0x0C: + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (lxb_char_t *) "\n", 1); + data += 1; + length += data - begin; + begin = data; + + continue; + + /* U+002A ASTERISK (*) */ + case 0x2A: + data++; + + if (data >= end) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto error; + } + + if (*data == 0x2F) { + tkz->pos--; + *tkz->pos = 0x00; + + data++; + length++; + + goto done; + } + + begin = data; + } + + /* U+002F Forward slash (/) */ + if (*data == 0x2F) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, (data - 1)); + + data++; + length++; + + goto done; + } + + continue; + } + + data++; + } + while (true); + +done: + + token->type = LXB_CSS_SYNTAX_TOKEN_COMMENT; + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '/'; + + return data; + +error: + + token->type = LXB_CSS_SYNTAX_TOKEN_COMMENT; + + lxb_css_syntax_token_base(token)->length = length; + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, NULL, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINCO); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * Whitespace + */ +const lxb_char_t * +lxb_css_syntax_state_whitespace(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *begin; + + token->type = LXB_CSS_SYNTAX_TOKEN_WHITESPACE; + + lxb_css_syntax_token_base(token)->begin = data; + + begin = data; + length = 0; + + do { + switch (*data) { + case 0x0D: + data++; + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + + tkz->pos[-1] = '\n'; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto done; + } + } + + if (*data != 0x0A) { + data--; + } + else { + length += 1; + } + + begin = data + 1; + break; + + case 0x0C: + length += (data + 1) - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (const lxb_char_t *) "\n", 1); + begin = data + 1; + break; + + case 0x09: + case 0x20: + case 0x0A: + break; + + default: + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + + data++; + + if (data >= end) { + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + break; + } + + begin = data; + } + } + while (true); + +done: + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * String token for U+0022 Quotation Mark (") and U+0027 Apostrophe (') + */ +const lxb_char_t * +lxb_css_syntax_state_string(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t mark; + lxb_status_t status; + const lxb_char_t *begin; + + lxb_css_syntax_token_base(token)->begin = data; + + mark = *data++; + begin = data; + length = 1; + + for (;; data++) { + if (data >= end) { + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto error; + } + + begin = data; + } + + switch (*data) { + case 0x00: + length += (data + 1) - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); + begin = data + 1; + break; + + /* + * U+000A LINE FEED + * U+000D CARRIAGE RETURN + * U+000C FORM FEED + */ + case 0x0A: + case 0x0D: + case 0x0C: + length += data - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_NEINST); + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_STRING; + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + + /* U+005C REVERSE SOLIDUS (\) */ + case 0x5C: + length += (data + 1) - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (const lxb_char_t *) "\\", 1); + goto error; + } + } + + data = lxb_css_syntax_state_escaped_string(tkz, data, &end, + &length); + if (data == NULL) { + return NULL; + } + + begin = data; + + data--; + break; + + default: + /* '"' or '\'' */ + if (*data == mark) { + length += (data + 1) - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + token->type = LXB_CSS_SYNTAX_TOKEN_STRING; + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, + data + 1); + } + + break; + } + } + + return data; + +error: + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, NULL, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINST); + + token->type = LXB_CSS_SYNTAX_TOKEN_STRING; + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * U+0023 NUMBER SIGN (#) + */ +const lxb_char_t * +lxb_css_syntax_state_hash(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, const lxb_char_t *data, + const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *begin; + lxb_css_syntax_token_t *delim; + + lxb_css_syntax_token_base(token)->begin = data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + length = 1; + + if (lxb_css_syntax_res_name_map[*data] == 0x00) { + if (*data == 0x00) { + goto hash; + } + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data != 0x5C) { + goto delim; + } + + begin = data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto push_delim; + } + } + + ch = *data; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + goto push_delim; + } + + length += 1; + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + } + +hash: + + token->type = LXB_CSS_SYNTAX_TOKEN_HASH; + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + +push_delim: + + delim = lxb_css_syntax_list_append_delim(tkz, begin, 1, '\\'); + if (delim == NULL) { + return NULL; + } + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '#'; + + return data; +} + +/* + * U+0028 LEFT PARENTHESIS (() + */ +const lxb_char_t * +lxb_css_syntax_state_lparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+0029 RIGHT PARENTHESIS ()) + */ +const lxb_char_t * +lxb_css_syntax_state_rparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+002B PLUS SIGN (+) + */ +const lxb_char_t * +lxb_css_syntax_state_plus(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_status_t status; + + lxb_css_syntax_token_base(token)->begin = data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '+'; + + return data; + } + } + + return lxb_css_syntax_state_plus_process(tkz, token, data, end); +} + +const lxb_char_t * +lxb_css_syntax_state_plus_process(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_status_t status; + const lxb_char_t *begin; + lxb_css_syntax_token_t *delim; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_number(token)->have_sign = true; + lxb_css_syntax_token_base(token)->length = 1; + + return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); + } + + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + begin = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + + if (data >= end || *data < 0x30 || *data > 0x39) { + goto push_delim; + } + + lxb_css_syntax_token_number(token)->have_sign = true; + lxb_css_syntax_token_base(token)->length = 2; + + return lxb_css_syntax_state_decimal(tkz, token, tkz->buffer, + tkz->buffer + sizeof(tkz->buffer), + data, end); + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_number(token)->have_sign = true; + lxb_css_syntax_token_base(token)->length = 2; + + return lxb_css_syntax_state_decimal(tkz, token, tkz->buffer, + tkz->buffer + sizeof(tkz->buffer), + data, end); + } + + push_delim: + + delim = lxb_css_syntax_list_append_delim(tkz, begin, 1, '.'); + if (delim == NULL) { + return NULL; + } + } + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '+'; + + return data; +} + +/* + * U+002C COMMA (,) + */ +const lxb_char_t * +lxb_css_syntax_state_comma(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_COMMA; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+002D HYPHEN-MINUS (-) + */ +const lxb_char_t * +lxb_css_syntax_state_minus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_status_t status; + + lxb_css_syntax_token_base(token)->begin = data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '-'; + + return data; + } + } + + return lxb_css_syntax_state_minus_process(tkz, token, data, end); +} + +const lxb_char_t * +lxb_css_syntax_state_minus_process(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *begin, *second; + lxb_css_syntax_token_t *delim; + lxb_css_syntax_token_number_t *number; + + unsigned minuses_len = 1; + static const lxb_char_t minuses[3] = "---"; + + /* Check for */ + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_base(token)->length = 1; + + data = lxb_css_syntax_state_consume_numeric(tkz, token, data, end); + + number = lxb_css_syntax_token_number(token); + number->num = -number->num; + + lxb_css_syntax_token_number(token)->have_sign = true; + + return data; + } + + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + begin = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto push_delim; + } + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_base(token)->length = 2; + + data = lxb_css_syntax_state_decimal(tkz, token, tkz->buffer, + tkz->buffer + sizeof(tkz->buffer), + data, end); + + number = lxb_css_syntax_token_number(token); + number->num = -number->num; + + lxb_css_syntax_token_number(token)->have_sign = true; + + return data; + } + + push_delim: + + delim = lxb_css_syntax_list_append_delim(tkz, begin, 1, '.'); + if (delim == NULL) { + return NULL; + } + + goto delim; + } + + second = data; + + /* U+002D HYPHEN-MINUS (-) */ + if (*data == 0x2D) { + data++; + + /* Check for */ + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + delim = lxb_css_syntax_list_append_delim(tkz, second, 1, '-'); + if (delim == NULL) { + return NULL; + } + + goto delim; + } + } + + if (*data == 0x2D) { + lxb_css_syntax_token_base(token)->length = 3; + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, 3); + + return lxb_css_syntax_state_ident_like_not_url(tkz, token, + ++data, end); + } + else if (*data == 0x3E) { + token->type = LXB_CSS_SYNTAX_TOKEN_CDC; + + lxb_css_syntax_token_base(token)->length = 3; + + return data + 1; + } + + minuses_len++; + } + + /* Check for */ + + if (lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START + || *data == 0x00) + { + lxb_css_syntax_token_base(token)->length = minuses_len; + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, minuses_len); + + return lxb_css_syntax_state_ident_like_not_url(tkz, token, data, end); + } + + length = 0; + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + begin = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim_rev_solidus; + } + + ch = *data; + + if (ch != 0x0A && ch != 0x0C && ch != 0x0D) { + length += 1; + goto ident; + } + + goto delim_rev_solidus; + } + + ch = *data; + + if (ch != 0x0A && ch != 0x0C && ch != 0x0D) { + length += 1; + goto ident; + } + + delim_rev_solidus: + + if (minuses_len == 2) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, second, 1, '-'); + } + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, begin, 1, '\\'); + + goto delim; + } + + if (minuses_len == 2) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, second, 0, '-'); + } + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '-'; + + return data; + +ident: + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, minuses_len); + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(token)->length = minuses_len + length; + + return lxb_css_syntax_state_ident_like_not_url(tkz, token, data, end); +} + +/* + * U+002E FULL STOP (.) + */ +const lxb_char_t * +lxb_css_syntax_state_full_stop(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_status_t status; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_number(token)->have_sign = false; + + data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_base(token)->length = 1; + + return lxb_css_syntax_state_decimal(tkz, token, tkz->buffer, + tkz->buffer + sizeof(tkz->buffer), + data, end); + } + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '.'; + + return data; +} + +/* + * U+003A COLON (:) + */ +const lxb_char_t * +lxb_css_syntax_state_colon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_COLON; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+003B SEMICOLON (;) + */ +const lxb_char_t * +lxb_css_syntax_state_semicolon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_SEMICOLON; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+003C LESS-THAN SIGN (<) + */ +const lxb_char_t * +lxb_css_syntax_state_less_sign(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *mark, *minus, *esc, *idnt; + lxb_css_syntax_token_t *ident; + + lxb_css_syntax_token_base(token)->begin = data++; + + if ((end - data) > 2) { + if (data[0] == '!' && data[1] == '-' && data[2] == '-') { + data += 3; + + token->type = LXB_CSS_SYNTAX_TOKEN_CDO; + lxb_css_syntax_token_base(token)->length = 4; + + return data; + } + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '<'; + + return data; + } + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + /* U+0021 EXCLAMATION MARK */ + if (*data != 0x21) { + goto delim; + } + + mark = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim_mark; + } + } + + /* U+002D HYPHEN-MINUS */ + if (*data != 0x2D) { + goto delim_mark; + } + + minus = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + + goto delim; + } + } + + /* U+002D HYPHEN-MINUS */ + if (*data == 0x2D) { + token->type = LXB_CSS_SYNTAX_TOKEN_CDO; + + lxb_css_syntax_token_base(token)->length = 4; + + return data + 1; + } + + length = 1; + idnt = data; + + if (lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START) { + goto ident_with_minus; + } + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + esc = data++; + length += 1; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim_esc; + } + + ch = *data; + + if (ch != 0x0A && ch != 0x0C && ch != 0x0D) { + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (const lxb_char_t *) "-", 1); + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + goto ident; + } + + delim_esc: + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, esc, 1, '\\'); + + goto delim; + } + + ch = *data--; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + + goto delim; + } + + data = lxb_css_syntax_state_escaped(tkz, data + 1, &end, &length); + if (data == NULL) { + return NULL; + } + } + else if (*data != 0x00) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 0, '-'); + + goto delim; + } + +ident_with_minus: + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, (const lxb_char_t *) "-", 1); + +ident: + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + + ident = lxb_css_syntax_state_token_create(tkz); + if (ident == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(ident)->begin = idnt; + lxb_css_syntax_token_base(ident)->length = length; + + data = lxb_css_syntax_state_ident_like_not_url(tkz, ident, data, end); + if (data == NULL) { + return NULL; + } + + goto delim; + +delim_mark: + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, mark, 1, '!'); + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '<'; + + return data; +} + +/* + * U+0040 COMMERCIAL AT (@) + */ +const lxb_char_t * +lxb_css_syntax_state_at(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *minus, *esc; + + unsigned minuses_len = 0; + static const lxb_char_t minuses[2] = "--"; + + token->type = LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD; + + lxb_css_syntax_token_base(token)->begin = data++; + lxb_css_syntax_token_base(token)->length = 1; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + if (lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START) { + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + } + + minus = data; + + /* U+002D HYPHEN-MINUS */ + if (*data == 0x2D) { + data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + goto delim; + } + } + + if (lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START + || *data == 0x00) + { + lxb_css_syntax_token_base(token)->length += 1; + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, 1); + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + } + else if (*data == 0x2D) { + lxb_css_syntax_token_base(token)->length += 2; + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, 2); + return lxb_css_syntax_state_consume_ident(tkz, token, + data + 1, end); + } + + minuses_len++; + } + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + esc = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim_esc; + } + } + + ch = *data; + + if (ch != 0x0A && ch != 0x0C && ch != 0x0D) { + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, minuses_len); + + length = 0; + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(token)->length += 1 + minuses_len + length; + + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + } + + goto delim_esc; + } + else if (*data != 0x00) { + if (minuses_len != 0) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 0, '-'); + } + + goto delim; + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, minuses, minuses_len); + + lxb_css_syntax_token_base(token)->length += minuses_len; + + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + +delim_esc: + + if (minuses_len != 0) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + } + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, esc, 1, '\\'); + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '@'; + + return data; +} + +/* + * U+005B LEFT SQUARE BRACKET ([) + */ +const lxb_char_t * +lxb_css_syntax_state_ls_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_LS_BRACKET; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+005C REVERSE SOLIDUS (\) + */ +const lxb_char_t * +lxb_css_syntax_state_rsolidus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + + lxb_css_syntax_token_base(token)->begin = data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + ch = *data; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + goto delim; + } + + length = 1; + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(token)->length = length; + + return lxb_css_syntax_state_ident_like(tkz, token, data, end); + +delim: + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_base(token)->length = 1; + lxb_css_syntax_token_delim(token)->character = '\\'; + + return data; +} + +/* + * U+005D RIGHT SQUARE BRACKET (]) + */ +const lxb_char_t * +lxb_css_syntax_state_rs_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+007B LEFT CURLY BRACKET ({) + */ +const lxb_char_t * +lxb_css_syntax_state_lc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_LC_BRACKET; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * U+007D RIGHT CURLY BRACKET (}) + */ +const lxb_char_t * +lxb_css_syntax_state_rc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +/* + * Numeric + */ +lxb_inline void +lxb_css_syntax_consume_numeric_set_int(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *start, const lxb_char_t *end) +{ + double num = lexbor_strtod_internal(start, (end - start), 0); + + token->type = LXB_CSS_SYNTAX_TOKEN_NUMBER; + + lxb_css_syntax_token_number(token)->is_float = false; + lxb_css_syntax_token_number(token)->num = num; +} + +lxb_inline void +lxb_css_syntax_consume_numeric_set_float(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *start, const lxb_char_t *end, + bool e_is_negative, int exponent, int e_digit) +{ + if (e_is_negative) { + exponent -= e_digit; + } + else { + exponent += e_digit; + } + + double num = lexbor_strtod_internal(start, (end - start), exponent); + + token->type = LXB_CSS_SYNTAX_TOKEN_NUMBER; + + lxb_css_syntax_token_number(token)->num = num; + lxb_css_syntax_token_number(token)->is_float = true; +} + +const lxb_char_t * +lxb_css_syntax_state_consume_before_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 0; + lxb_css_syntax_token_number(token)->have_sign = false; + + return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *begin; + + lxb_char_t *buf_start = tkz->buffer; + lxb_char_t *buf_end = buf_start + sizeof(tkz->buffer); + + begin = data; + length = 0; + + do { + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data < 0x30 || *data > 0x39) { + length += data - begin; + break; + } + + if (buf_start != buf_end) { + *buf_start++ = *data; + } + + if (++data == end) { + length += data - begin; + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_token_base(token)->length += length; + + lxb_css_syntax_consume_numeric_set_int(tkz, token, tkz->buffer, + buf_start); + return data; + } + + begin = data; + } + } + while (true); + + lxb_css_syntax_token_base(token)->length += length; + + /* U+002E FULL STOP (.) */ + if (*data != 0x2E) { + lxb_css_syntax_consume_numeric_set_int(tkz, token, tkz->buffer, + buf_start); + + return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, + data, end); + } + + begin = data++; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim; + } + } + + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_base(token)->length += 1; + + return lxb_css_syntax_state_decimal(tkz, token, buf_start, buf_end, + data, end); + } + +delim: + + lxb_css_syntax_consume_numeric_set_int(tkz, token, tkz->buffer, buf_start); + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, begin, 1, '.'); + + return data; +} + +static const lxb_char_t * +lxb_css_syntax_state_decimal(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + lxb_char_t *buf_start, lxb_char_t *buf_end, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + bool e_is_negative; + int exponent, e_digit; + lxb_char_t ch, by; + lxb_status_t status; + const lxb_char_t *last, *begin; + lxb_css_syntax_token_t *t_str; + lxb_css_syntax_token_string_t *str; + + exponent = 0; + begin = data; + length = lxb_css_syntax_token_base(token)->length; + + str = lxb_css_syntax_token_dimension_string(token); + t_str = (lxb_css_syntax_token_t *) (void *) str; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + do { + if (buf_start != buf_end) { + *buf_start++ = *data; + exponent -= 1; + } + + data++; + + if (data >= end) { + length += data - begin; + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_token_base(token)->length = length; + + lxb_css_syntax_consume_numeric_set_float(tkz, token, tkz->buffer, + buf_start, 0, exponent, 0); + return data; + } + + begin = data; + } + } + while (*data >= 0x30 && *data <= 0x39); + + length += data - begin; + + lxb_css_syntax_token_base(token)->length = length; + lxb_css_syntax_token_base(str)->begin = data; + + ch = *data; + + /* U+0045 Latin Capital Letter (E) or U+0065 Latin Small Letter (e) */ + if (ch != 0x45 && ch != 0x65) { + lxb_css_syntax_consume_numeric_set_float(tkz, token, tkz->buffer, + buf_start, 0, exponent, 0); + + return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, + data, end); + } + + e_digit = 0; + e_is_negative = false; + + lxb_css_syntax_token_base(t_str)->length = 1; + + if (++data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_consume_numeric_set_float(tkz, token, tkz->buffer, + buf_start, 0, exponent, 0); + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, &ch, 1); + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + data = lxb_css_syntax_state_dimension_set(tkz, token, data); + + lxb_css_syntax_token_base(token)->length += + lxb_css_syntax_token_base(t_str)->length; + return data; + } + } + + switch (*data) { + /* U+002D HYPHEN-MINUS (-) */ + case 0x2D: + e_is_negative = true; + /* fall through */ + + /* U+002B PLUS SIGN (+) */ + case 0x2B: + last = data++; + by = *last; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto dimension; + } + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data < 0x30 || *data > 0x39) { + goto dimension; + } + + length += 1; + break; + + default: + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data < 0x30 || *data > 0x39) { + lxb_css_syntax_consume_numeric_set_float(tkz, token, + tkz->buffer, buf_start, + 0, exponent, 0); + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, &ch, 1); + + data = lxb_css_syntax_state_consume_ident(tkz, t_str, + data, end); + if (begin == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(token)->length = length + + lxb_css_syntax_token_base(t_str)->length; + return data; + } + + break; + } + + length += 1; + begin = data; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + do { + e_digit = (*data - 0x30) + e_digit * 0x0A; + + if (++data == end) { + length += data - begin; + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_token_base(token)->length = length; + + lxb_css_syntax_consume_numeric_set_float(tkz, token, tkz->buffer, buf_start, + e_is_negative, exponent, e_digit); + return data; + } + + begin = data; + } + } + while(*data >= 0x30 && *data <= 0x39); + + length += data - begin; + + lxb_css_syntax_token_base(token)->length = length; + + lxb_css_syntax_consume_numeric_set_float(tkz, token, tkz->buffer, buf_start, + e_is_negative, exponent, e_digit); + + return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, + data, end); + +dimension: + + lxb_css_syntax_consume_numeric_set_float(tkz, token, + tkz->buffer, buf_start, + 0, exponent, 0); + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, &ch, 1); + + if (by == '-') { + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, &by, 1); + + lxb_css_syntax_token_base(t_str)->length += 1; + + data = lxb_css_syntax_state_consume_ident(tkz, t_str, data, end); + + lxb_css_syntax_token_base(token)->length = length + + lxb_css_syntax_token_base(t_str)->length; + return data; + } + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, last, (data >= end), '+'); + + lxb_css_syntax_token_base(token)->length = length + + lxb_css_syntax_token_base(t_str)->length; + + return lxb_css_syntax_state_dimension_set(tkz, token, data); +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric_name_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + bool have_minus; + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *esc, *minus; + lxb_css_syntax_token_t *t_str; + lxb_css_syntax_token_string_t *str; + + str = lxb_css_syntax_token_dimension_string(token); + t_str = (lxb_css_syntax_token_t *) (void *) str; + + lxb_css_syntax_token_base(t_str)->begin = data; + + ch = *data; + + if (lxb_css_syntax_res_name_map[ch] == LXB_CSS_SYNTAX_RES_NAME_START + || ch == 0x00) + { + lxb_css_syntax_token_base(t_str)->length = 0; + goto dimension; + } + + /* U+0025 PERCENTAGE SIGN (%) */ + if (ch == 0x25) { + token->type = LXB_CSS_SYNTAX_TOKEN_PERCENTAGE; + + lxb_css_syntax_token_base(token)->length += 1; + + return data + 1; + } + + have_minus = false; + minus = data; + + /* U+002D HYPHEN-MINUS */ + if (ch == 0x2D) { + data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + return data; + } + } + + ch = *data; + + if (lxb_css_syntax_res_name_map[ch] == LXB_CSS_SYNTAX_RES_NAME_START + || ch == 0x2D || ch == 0x00) + { + lxb_css_syntax_token_base(t_str)->length = 1; + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (const lxb_char_t *) "-", 1); + goto dimension; + } + + have_minus = true; + } + + esc = data; + + /* U+005C REVERSE SOLIDUS (\) */ + if (ch == 0x5C) { + data++; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto delim_rev_solidus; + } + } + + ch = *data; + + if (ch != 0x0A && ch != 0x0C && ch != 0x0D) { + length = 1; + + if (have_minus) { + length += 1; + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + (const lxb_char_t *) "-", 1); + } + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + lxb_css_syntax_token_base(t_str)->length = length; + + goto dimension; + } + + delim_rev_solidus: + + if (have_minus) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 1, '-'); + } + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, esc, 1, '\\'); + + return data; + } + + if (have_minus) { + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, minus, 0, '-'); + } + + return data; + +dimension: + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + data = lxb_css_syntax_state_consume_ident(tkz, t_str, data, end); + + lxb_css_syntax_token_base(token)->length += + lxb_css_syntax_token_base(t_str)->length; + + return data; +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_ident(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *begin; + + begin = data; + length = 0; + + for (;; data++) { + if (data >= end) { + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + + begin = data; + } + + if (lxb_css_syntax_res_name_map[*data] == 0x00) { + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + begin = data; + + if (++data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + goto push_delim_last; + } + } + + if (*data == 0x0A || *data == 0x0C || *data == 0x0D) { + goto push_delim_last; + } + + length += 1; + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + begin = data--; + } + else if (*data == 0x00) { + length += (data + 1) - begin; + + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); + begin = data + 1; + } + else { + if (begin < data) { + length += data - begin; + + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + } + } + + return data; + +push_delim_last: + + lxb_css_syntax_token_base(token)->length += length; + + LXB_CSS_SYNTAX_DELIM_APPEND(tkz, begin, 1, '\\'); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +const lxb_char_t * +lxb_css_syntax_state_ident_like_begin(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 0; + + return lxb_css_syntax_state_ident_like(tkz, token, data, end); +} + +static const lxb_char_t * +lxb_css_syntax_state_ident_like(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *begin, *ws_begin; + lxb_css_syntax_token_t *ws; + lxb_css_syntax_token_string_t *str, *ws_str; + static const lxb_char_t url[] = "url"; + + data = lxb_css_syntax_state_consume_ident(tkz, token, data, end); + + end = tkz->in_end; + + if (data >= end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + token->type = LXB_CSS_SYNTAX_TOKEN_IDENT; + return data; + } + } + + if (data < end && *data == '(') { + data++; + + lxb_css_syntax_token_base(token)->length += 1; + + str = lxb_css_syntax_token_string(token); + + if (str->length == 3 && lexbor_str_data_casecmp(str->data, url)) { + begin = data; + length = 0; + + tkz->pos += str->length + 1; + ws_begin = tkz->pos; + + do { + if (data >= end) { + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + begin = data; + goto with_ws; + } + + begin = data; + } + + ch = *data; + + if (lexbor_utils_whitespace(ch, !=, &&)) { + /* U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE (') */ + if (ch == 0x22 || ch == 0x27) { + goto with_ws; + } + + tkz->pos = tkz->start; + length += data - begin; + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_url(tkz, token, data, end); + } + + data++; + } + while (true); + } + + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + return data; + } + + token->type = LXB_CSS_SYNTAX_TOKEN_IDENT; + + return data; + +with_ws: + + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + if (ws_begin != tkz->pos || begin < data) { + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + if (tkz->pos >= tkz->end) { + if (lxb_css_syntax_string_realloc(tkz, 1024) != LXB_STATUS_OK) { + return NULL; + } + } + + str->data = tkz->start; + *tkz->pos = 0x00; + + ws = lxb_css_syntax_state_token_create(tkz); + if (ws == NULL) { + return NULL; + } + + ws->type = LXB_CSS_SYNTAX_TOKEN_WHITESPACE; + + lxb_css_syntax_token_base(ws)->begin = begin; + lxb_css_syntax_token_base(ws)->length = length; + + ws_str = lxb_css_syntax_token_string(ws); + + ws_str->data = tkz->start + str->length + 1; + ws_str->length = tkz->pos - ws_str->data; + } + + tkz->pos = tkz->start; + + return data; +} + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url_begin(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_css_syntax_token_base(token)->begin = data; + lxb_css_syntax_token_base(token)->length = 0; + + return lxb_css_syntax_state_ident_like_not_url(tkz, token, data, end); +} + +static const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + data = lxb_css_syntax_state_consume_ident(tkz, token, data, end); + if (data == NULL) { + return NULL; + } + + end = tkz->in_end; + + if (data < end && *data == '(') { + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + lxb_css_syntax_token_base(token)->length += 1; + + return data + 1; + } + + token->type = LXB_CSS_SYNTAX_TOKEN_IDENT; + + return data; +} + +/* + * URL + */ +static const lxb_char_t * +lxb_css_syntax_state_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_char_t ch; + lxb_status_t status; + const lxb_char_t *begin; + + status = LXB_STATUS_OK; + + *tkz->pos = 0x00; + + begin = data; + length = 0; + + do { + if (data >= end) { + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINUR); + + token->type = LXB_CSS_SYNTAX_TOKEN_URL; + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + + begin = data; + } + + switch (*data) { + /* U+0000 NULL (\0) */ + case 0x00: + if (begin < data) { + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + LXB_CSS_SYNTAX_STR_APPEND_LEN(tkz, status, + lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); + + data += 1; + length += data - begin; + begin = data; + + continue; + + /* U+0029 RIGHT PARENTHESIS ()) */ + case 0x29: + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + token->type = LXB_CSS_SYNTAX_TOKEN_URL; + + lxb_css_syntax_token_base(token)->length += length + 1; + + return lxb_css_syntax_state_string_set(tkz, token, data + 1); + + /* + * U+0022 QUOTATION MARK (") + * U+0027 APOSTROPHE (') + * U+0028 LEFT PARENTHESIS (() + * U+000B LINE TABULATION + * U+007F DELETE + */ + case 0x22: + case 0x27: + case 0x28: + case 0x0B: + case 0x7F: + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_QOINUR); + + lxb_css_syntax_token_base(token)->length += length + 1; + + return lxb_css_syntax_state_bad_url(tkz, token, data + 1, end); + + /* U+005C REVERSE SOLIDUS (\) */ + case 0x5C: + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + begin = ++data; + + if (data == end) { + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_WRESINUR); + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_URL; + + lxb_css_syntax_token_base(token)->length += length + 1; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + } + + ch = *data; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_WRESINUR); + + lxb_css_syntax_token_base(token)->length += length + 1; + + return lxb_css_syntax_state_bad_url(tkz, token, data, end); + } + + data = lxb_css_syntax_state_escaped(tkz, data, &end, &length); + if (data == NULL) { + return NULL; + } + + begin = data--; + length += 1; + + break; + + /* + * U+0009 CHARACTER TABULATION (tab) + * U+000A LINE FEED (LF) + * U+000C FORM FEED (FF) + * U+000D CARRIAGE RETURN (CR) + * U+0020 SPACE + */ + case 0x09: + case 0x0A: + case 0x0C: + case 0x0D: + case 0x20: + if (begin < data) { + length += data - begin; + LXB_CSS_SYNTAX_STR_APPEND(tkz, status, begin, data); + } + + begin = ++data; + length += 1; + + do { + if (data == end) { + length += data - begin; + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINUR); + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_URL; + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_string_set(tkz, token, data); + } + + begin = data; + } + + ch = *data; + + if (lexbor_utils_whitespace(ch, !=, &&)) { + length += data - begin; + + /* U+0029 RIGHT PARENTHESIS ()) */ + if (*data == 0x29) { + token->type = LXB_CSS_SYNTAX_TOKEN_URL; + + lxb_css_syntax_token_base(token)->length += length + 1; + + return lxb_css_syntax_state_string_set(tkz, token, + data + 1); + } + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_bad_url(tkz, token, + data, end); + } + + data++; + } + while (true); + + default: + /* + * Inclusive: + * U+0000 NULL and U+0008 BACKSPACE or + * U+000E SHIFT OUT and U+001F INFORMATION SEPARATOR ONE + */ + if ((*data <= 0x08) + || (*data >= 0x0E && *data <= 0x1F)) + { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_QOINUR); + + lxb_css_syntax_token_base(token)->length += length; + + return lxb_css_syntax_state_bad_url(tkz, token, + data + 1, end); + } + + break; + } + + data++; + } + while (true); + + return data; +} + +/* + * Bad URL + */ +static const lxb_char_t * +lxb_css_syntax_state_bad_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + size_t length; + lxb_status_t status; + const lxb_char_t *begin; + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_URL; + + if(lxb_css_syntax_state_string_set(tkz, token, data) == NULL) { + return NULL; + } + + begin = data; + length = 0; + + do { + if (data >= end) { + length += data - begin; + + LXB_CSS_SYNTAX_NEXT_CHUNK(tkz, status, data, end); + if (data >= end) { + lxb_css_syntax_token_base(token)->length += length; + return data; + } + + begin = data; + } + + /* U+0029 RIGHT PARENTHESIS ()) */ + if (*data == 0x29) { + data++; + length += data - begin; + + lxb_css_syntax_token_base(token)->length += length; + + return data; + } + /* U+005C REVERSE SOLIDUS (\) */ + else if (*data == 0x5C) { + data++; + + if (data >= end) { + continue; + } + } + + data++; + } + while (true); + + return data; +} + +lxb_inline lxb_status_t +lxb_css_syntax_string_append_rep(lxb_css_syntax_tokenizer_t *tkz) +{ + return lxb_css_syntax_string_append(tkz, lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); +} + +static const lxb_char_t * +lxb_css_syntax_state_escaped(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, + const lxb_char_t **end, size_t *length) +{ + uint32_t cp; + unsigned count; + lxb_status_t status; + + cp = 0; + + for (count = 0; count < 6; count++, data++) { + if (data >= *end) { + status = lxb_css_syntax_tokenizer_next_chunk(tkz, &data, end); + if (status != LXB_STATUS_OK) { + return NULL; + } + + if (data >= *end) { + if (count == 0) { + return *end; + } + + break; + } + } + + if (lexbor_str_res_map_hex[*data] == 0xFF) { + if (count == 0) { + *length += 1; + + if (*data == 0x00) { + status = lxb_css_syntax_string_append_rep(tkz); + if (status != LXB_STATUS_OK) { + return NULL; + } + + return data + 1; + } + + status = lxb_css_syntax_string_append(tkz, data, 1); + if (status != LXB_STATUS_OK) { + return NULL; + } + + return data + 1; + } + + switch (*data) { + case 0x0D: + data++; + *length += 1; + + status = lxb_css_syntax_tokenizer_next_chunk(tkz, &data, + end); + if (status != LXB_STATUS_OK) { + return NULL; + } + + if (data >= *end) { + break; + } + + if (*data == 0x0A) { + data++; + *length += 1; + } + + break; + + case 0x09: + case 0x20: + case 0x0A: + case 0x0C: + data++; + *length += 1; + break; + } + + break; + } + + cp <<= 4; + cp |= lexbor_str_res_map_hex[*data]; + } + + if ((tkz->end - tkz->pos) < 5) { + if (lxb_css_syntax_string_realloc(tkz, 1024) != LXB_STATUS_OK) { + return NULL; + } + } + + lxb_css_syntax_codepoint_to_ascii(tkz, cp); + + *length += count; + + return data; +} + +static const lxb_char_t * +lxb_css_syntax_state_escaped_string(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, + const lxb_char_t **end, size_t *length) +{ + lxb_status_t status; + + /* U+000D CARRIAGE RETURN */ + if (*data == 0x0D) { + data++; + *length += 1; + + if (data >= *end) { + status = lxb_css_syntax_tokenizer_next_chunk(tkz, &data, end); + if (status != LXB_STATUS_OK) { + return NULL; + } + + if (data >= *end) { + return data; + } + } + + /* U+000A LINE FEED */ + if (*data == 0x0A) { + data++; + *length += 1; + } + + return data; + } + + if (*data == 0x00) { + status = lxb_css_syntax_string_append_rep(tkz); + if (status != LXB_STATUS_OK) { + return NULL; + } + + *length += 1; + + return data + 1; + } + + if (*data == 0x0A || *data == 0x0C) { + *length += 1; + + return data + 1; + } + + return lxb_css_syntax_state_escaped(tkz, data, end, length); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/syntax.c b/ext/dom/lexbor/lexbor/css/syntax/syntax.c new file mode 100644 index 0000000000000..8d54345ee5d49 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/syntax.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2018-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/parser.h" + +#include "lexbor/core/str.h" + +#define LEXBOR_STR_RES_MAP_HEX +#define LEXBOR_STR_RES_MAP_HEX_TO_CHAR_LOWERCASE +#define LEXBOR_STR_RES_CHAR_TO_TWO_HEX_VALUE_LOWERCASE +#define LEXBOR_STR_RES_ANSI_REPLACEMENT_CHARACTER +#include "lexbor/core/str_res.h" + +#define LXB_CSS_SYNTAX_RES_NAME_MAP +#include "lexbor/css/syntax/res.h" + + +static const lexbor_str_t lxb_str_ws = lexbor_str(" "); + + +lxb_status_t +lxb_css_syntax_parse_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *cb, + const lxb_char_t *data, size_t length, + void *ctx, bool top_level) +{ + lxb_status_t status; + lxb_css_syntax_rule_t *rule; + + if (lxb_css_parser_is_running(parser)) { + parser->status = LXB_STATUS_ERROR_WRONG_STAGE; + return parser->status; + } + + lxb_css_parser_clean(parser); + + lxb_css_parser_buffer_set(parser, data, length); + + rule = lxb_css_syntax_parser_list_rules_push(parser, NULL, NULL, cb, + ctx, top_level, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + status = parser->status; + goto end; + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + status = lxb_css_syntax_parser_run(parser); + if (status != LXB_STATUS_OK) { + /* Destroy StyleSheet. */ + } + +end: + + parser->stage = LXB_CSS_PARSER_END; + + return status; +} + +lxb_status_t +lxb_css_syntax_stack_expand(lxb_css_parser_t *parser, size_t count) +{ + size_t length, cur_len, size; + lxb_css_syntax_rule_t *p; + + if ((parser->rules + count) >= parser->rules_end) { + cur_len = parser->rules - parser->rules_begin; + + length = cur_len + count + 1024; + size = length * sizeof(lxb_css_syntax_rule_t); + + p = lexbor_realloc(parser->rules_begin, size); + if (p == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->rules_begin = p; + parser->rules_end = p + length; + parser->rules = p + cur_len; + } + + return LXB_STATUS_OK; +} + +void +lxb_css_syntax_codepoint_to_ascii(lxb_css_syntax_tokenizer_t *tkz, + lxb_codepoint_t cp) +{ + /* + * Zero, or is for a surrogate, or is greater than + * the maximum allowed code point (tkz->num > 0x10FFFF). + */ + if (cp == 0 || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) { + memcpy(tkz->pos, lexbor_str_res_ansi_replacement_character, 3); + + tkz->pos += 3; + *tkz->pos = '\0'; + + return; + } + + lxb_char_t *data = tkz->pos; + + if (cp <= 0x0000007F) { + /* 0xxxxxxx */ + data[0] = (lxb_char_t) cp; + + tkz->pos += 1; + } + else if (cp <= 0x000007FF) { + /* 110xxxxx 10xxxxxx */ + data[0] = (char)(0xC0 | (cp >> 6 )); + data[1] = (char)(0x80 | (cp & 0x3F)); + + tkz->pos += 2; + } + else if (cp <= 0x0000FFFF) { + /* 1110xxxx 10xxxxxx 10xxxxxx */ + data[0] = (char)(0xE0 | ((cp >> 12))); + data[1] = (char)(0x80 | ((cp >> 6 ) & 0x3F)); + data[2] = (char)(0x80 | ( cp & 0x3F)); + + tkz->pos += 3; + } + else if (cp <= 0x001FFFFF) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + data[0] = (char)(0xF0 | ( cp >> 18)); + data[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + data[2] = (char)(0x80 | ((cp >> 6 ) & 0x3F)); + data[3] = (char)(0x80 | ( cp & 0x3F)); + + tkz->pos += 4; + } + + *tkz->pos = '\0'; +} + +lxb_status_t +lxb_css_syntax_ident_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t ch; + lxb_status_t status; + const char **hex_map; + const lxb_char_t *p = data, *end; + + static const lexbor_str_t str_s = lexbor_str("\\"); + + p = data; + end = data + length; + hex_map = lexbor_str_res_char_to_two_hex_value_lowercase; + + while (p < end) { + ch = *p; + + if (lxb_css_syntax_res_name_map[ch] == 0x00) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_s.data, str_s.length, ctx, status); + lexbor_serialize_write(cb, hex_map[ch], 2, ctx, status); + + data = ++p; + + if (p < end && lexbor_str_res_map_hex[*p] != 0xff) { + lexbor_serialize_write(cb, lxb_str_ws.data, + lxb_str_ws.length, ctx, status); + } + + continue; + } + + p++; + } + + if (data < p) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t ch; + lxb_status_t status; + const char **hex_map; + const lxb_char_t *p, *end; + + static const lexbor_str_t str_s = lexbor_str("\\"); + static const lexbor_str_t str_dk = lexbor_str("\""); + static const lexbor_str_t str_ds = lexbor_str("\\\\"); + static const lexbor_str_t str_dks = lexbor_str("\\\""); + + p = data; + end = data + length; + hex_map = lexbor_str_res_char_to_two_hex_value_lowercase; + + lexbor_serialize_write(cb, str_dk.data, str_dk.length, ctx, status); + + while (p < end) { + ch = *p; + + if (lxb_css_syntax_res_name_map[ch] == 0x00) { + switch (ch) { + case '\\': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_ds.data, str_ds.length, + ctx, status); + break; + + case '"': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_dks.data, str_dks.length, + ctx, status); + break; + + case '\n': + case '\t': + case '\r': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_s.data, str_s.length, + ctx, status); + lexbor_serialize_write(cb, hex_map[ch], 2, ctx, status); + + p++; + + if (p < end && lexbor_str_res_map_hex[*p] != 0xff) { + lexbor_serialize_write(cb, lxb_str_ws.data, + lxb_str_ws.length, ctx, status); + } + + data = p; + continue; + + default: + p++; + continue; + } + + data = ++p; + continue; + } + + p++; + } + + if (data < p) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + } + + lexbor_serialize_write(cb, str_dk.data, str_dk.length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_ident_or_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_char_t *p, *end; + + p = data; + end = data + length; + + while (p < end) { + if (lxb_css_syntax_res_name_map[*p++] == 0x00) { + return lxb_css_syntax_string_serialize(data, length, cb, ctx); + } + } + + return cb(data, length, ctx); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/token.c b/ext/dom/lexbor/lexbor/css/syntax/token.c new file mode 100644 index 0000000000000..b757acfcf4dca --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/token.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2018-2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/shs.h" +#include "lexbor/core/conv.h" +#include "lexbor/core/serialize.h" +#include "lexbor/core/print.h" + +#include "lexbor/css/parser.h" +#include "lexbor/css/syntax/token.h" +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/state_res.h" + +#define LXB_CSS_SYNTAX_TOKEN_RES_NAME_SHS_MAP +#include "lexbor/css/syntax/token_res.h" + +#define LEXBOR_STR_RES_MAP_HEX +#define LEXBOR_STR_RES_ANSI_REPLACEMENT_CHARACTER +#include "lexbor/core/str_res.h" + + +lxb_css_syntax_token_t * +lxb_css_syntax_tokenizer_token(lxb_css_syntax_tokenizer_t *tkz); + +lxb_status_t +lxb_css_syntax_tokenizer_cache_push(lxb_css_syntax_tokenizer_cache_t *cache, + lxb_css_syntax_token_t *value); + + +typedef struct { + lexbor_str_t *str; + lexbor_mraw_t *mraw; +} +lxb_css_syntax_token_ctx_t; + + +static lxb_status_t +lxb_css_syntax_token_str_cb(const lxb_char_t *data, size_t len, void *ctx); + + +lxb_css_syntax_token_t * +lxb_css_syntax_token(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz->cache_pos < tkz->cache->length + && (tkz->prepared == 0 || tkz->cache_pos < tkz->prepared)) + { + return tkz->cache->list[tkz->cache_pos]; + } + + return lxb_css_syntax_tokenizer_token(tkz); +} + +lxb_css_syntax_token_t * +lxb_css_syntax_token_next(lxb_css_syntax_tokenizer_t *tkz) +{ + return lxb_css_syntax_tokenizer_token(tkz); +} + +void +lxb_css_syntax_token_consume(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_css_syntax_token_t *token; + + if (tkz->cache_pos < tkz->cache->length) { + if (tkz->prepared != 0 && tkz->cache_pos >= tkz->prepared) { + return; + } + + token = tkz->cache->list[tkz->cache_pos]; + + lxb_css_syntax_token_string_free(tkz, token); + lexbor_dobject_free(tkz->tokens, token); + + tkz->cache_pos += 1; + + if (tkz->cache_pos >= tkz->cache->length) { + tkz->cache->length = 0; + tkz->cache_pos = 0; + } + } +} + +void +lxb_css_syntax_token_consume_n(lxb_css_syntax_tokenizer_t *tkz, unsigned count) +{ + while (count != 0) { + count--; + lxb_css_syntax_token_consume(tkz); + } +} + +lxb_status_t +lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw) +{ + size_t length; + + length = token->length + 1; + + if (length > str->length) { + if (str->data == NULL) { + str->data = lexbor_mraw_alloc(mraw, length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + str->length = 0; + } + else { + if (lexbor_str_realloc(str, mraw, length) == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + } + + /* + 1 = '\0' */ + memcpy(str->data, token->data, length); + + str->length = token->length; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_token_string_make(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token) +{ + lxb_char_t *data; + lxb_css_syntax_token_string_t *token_string; + + if (token->type >= LXB_CSS_SYNTAX_TOKEN_IDENT + && token->type <= LXB_CSS_SYNTAX_TOKEN_WHITESPACE) + { + token_string = lxb_css_syntax_token_string(token); + goto copy; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_DIMENSION) { + token_string = lxb_css_syntax_token_dimension_string(token); + goto copy; + } + + return LXB_STATUS_OK; + +copy: + + data = lexbor_mraw_alloc(tkz->mraw, token_string->length + 1); + if (data == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return tkz->status; + } + + /* + 1 = '\0' */ + memcpy(data, token_string->data, token_string->length + 1); + + token_string->data = data; + token->cloned = true; + + return LXB_STATUS_OK; +} + +lxb_css_syntax_token_t * +lxb_css_syntax_token_cached_create(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + lxb_css_syntax_token_t *token; + + token = lexbor_dobject_alloc(tkz->tokens); + if (token == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return NULL; + } + + status = lxb_css_syntax_tokenizer_cache_push(tkz->cache, token); + if (status != LXB_STATUS_OK) { + tkz->status = status; + return NULL; + } + + token->cloned = false; + + return token; +} + + +void +lxb_css_syntax_token_string_free(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token) +{ + lxb_css_syntax_token_string_t *token_string; + + if (token->cloned) { + if (token->type == LXB_CSS_SYNTAX_TOKEN_DIMENSION) { + token_string = lxb_css_syntax_token_dimension_string(token); + } + else { + token_string = lxb_css_syntax_token_string(token); + } + + lexbor_mraw_free(tkz->mraw, (lxb_char_t *) token_string->data); + } +} + +const lxb_char_t * +lxb_css_syntax_token_type_name_by_id(lxb_css_syntax_token_type_t type) +{ + switch (type) { + case LXB_CSS_SYNTAX_TOKEN_IDENT: + return (lxb_char_t *) "ident"; + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + return (lxb_char_t *) "function"; + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + return (lxb_char_t *) "at-keyword"; + case LXB_CSS_SYNTAX_TOKEN_HASH: + return (lxb_char_t *) "hash"; + case LXB_CSS_SYNTAX_TOKEN_STRING: + return (lxb_char_t *) "string"; + case LXB_CSS_SYNTAX_TOKEN_BAD_STRING: + return (lxb_char_t *) "bad-string"; + case LXB_CSS_SYNTAX_TOKEN_URL: + return (lxb_char_t *) "url"; + case LXB_CSS_SYNTAX_TOKEN_BAD_URL: + return (lxb_char_t *) "bad-url"; + case LXB_CSS_SYNTAX_TOKEN_DELIM: + return (lxb_char_t *) "delim"; + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + return (lxb_char_t *) "number"; + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + return (lxb_char_t *) "percentage"; + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + return (lxb_char_t *) "dimension"; + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + return (lxb_char_t *) "whitespace"; + case LXB_CSS_SYNTAX_TOKEN_CDO: + return (lxb_char_t *) "CDO"; + case LXB_CSS_SYNTAX_TOKEN_CDC: + return (lxb_char_t *) "CDC"; + case LXB_CSS_SYNTAX_TOKEN_COLON: + return (lxb_char_t *) "colon"; + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return (lxb_char_t *) "semicolon"; + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return (lxb_char_t *) "comma"; + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + return (lxb_char_t *) "left-square-bracket"; + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + return (lxb_char_t *) "right-square-bracket"; + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + return (lxb_char_t *) "left-parenthesis"; + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + return (lxb_char_t *) "right-parenthesis"; + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + return (lxb_char_t *) "left-curly-bracket"; + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + return (lxb_char_t *) "right-curly-bracket"; + case LXB_CSS_SYNTAX_TOKEN_COMMENT: + return (lxb_char_t *) "comment"; + case LXB_CSS_SYNTAX_TOKEN__EOF: + return (lxb_char_t *) "end-of-file"; + case LXB_CSS_SYNTAX_TOKEN__END: + return (lxb_char_t *) "end"; + default: + return (lxb_char_t *) "undefined"; + } +} + +lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_id_by_name(const lxb_char_t *type_name, size_t len) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_syntax_token_res_name_shs_map, + type_name, len); + if (entry == NULL) { + return LXB_CSS_SYNTAX_TOKEN_UNDEF; + } + + return (lxb_css_syntax_token_type_t) (uintptr_t) entry->value; +} + + +lxb_status_t +lxb_css_syntax_token_serialize(const lxb_css_syntax_token_t *token, + lxb_css_syntax_token_cb_f cb, void *ctx) +{ + size_t len; + lxb_status_t status; + lxb_char_t buf[128]; + const lxb_css_syntax_token_string_t *str; + const lxb_css_syntax_token_dimension_t *dim; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + buf[0] = token->types.delim.character; + buf[1] = 0x00; + + return cb(buf, 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + return cb(buf, len, ctx); + + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + status = cb(buf, len, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "%", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_CDO: + return cb((lxb_char_t *) "", 3, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COLON: + return cb((lxb_char_t *) ":", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return cb((lxb_char_t *) ";", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return cb((lxb_char_t *) ",", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + return cb((lxb_char_t *) "[", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + return cb((lxb_char_t *) "]", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + return cb((lxb_char_t *) "(", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + return cb((lxb_char_t *) ")", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + return cb((lxb_char_t *) "{", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + return cb((lxb_char_t *) "}", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = cb((lxb_char_t *) "#", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + status = cb((lxb_char_t *) "@", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + case LXB_CSS_SYNTAX_TOKEN_IDENT: + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "(", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_STRING: + case LXB_CSS_SYNTAX_TOKEN_BAD_STRING: { + status = cb((lxb_char_t *) "\"", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + const lxb_char_t *begin = token->types.string.data; + const lxb_char_t *end = begin + token->types.string.length; + + const lxb_char_t *ptr = begin; + + for (; begin < end; begin++) { + /* 0x5C; '\'; Inverse/backward slash */ + if (*begin == 0x5C) { + begin += 1; + + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (begin == end) { + status = cb((const lxb_char_t *) "\\", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ptr = begin; + + break; + } + + begin -= 1; + ptr = begin; + } + /* 0x22; '"'; Only quotes above */ + else if (*begin == 0x22) { + if (ptr != begin) { + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + status = cb((const lxb_char_t *) "\\", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ptr = begin; + } + } + + if (ptr != begin) { + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + return cb((const lxb_char_t *) "\"", 1, ctx); + } + + case LXB_CSS_SYNTAX_TOKEN_URL: + case LXB_CSS_SYNTAX_TOKEN_BAD_URL: + status = cb((lxb_char_t *) "url(", 4, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) ")", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COMMENT: + status = cb((lxb_char_t *) "/*", 2, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "*/", 2, ctx); + + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + status = cb(buf, len, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + dim = &token->types.dimension; + + return cb(dim->str.data, dim->str.length, ctx); + + case LXB_CSS_SYNTAX_TOKEN__EOF: + return cb((lxb_char_t *) "END-OF-FILE", 11, ctx); + + case LXB_CSS_SYNTAX_TOKEN__END: + return cb((lxb_char_t *) "END", 3, ctx); + + default: + return LXB_STATUS_ERROR; + } +} + +lxb_status_t +lxb_css_syntax_token_serialize_str(const lxb_css_syntax_token_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw) +{ + lxb_css_syntax_token_ctx_t ctx; + + ctx.str = str; + ctx.mraw = mraw; + + if (str->data == NULL) { + lexbor_str_init(str, mraw, 1); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + return lxb_css_syntax_token_serialize(token, lxb_css_syntax_token_str_cb, + &ctx); +} + +static lxb_status_t +lxb_css_syntax_token_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx) +{ + lxb_char_t *ptr; + lxb_css_syntax_token_ctx_t *ctx = (lxb_css_syntax_token_ctx_t *) cb_ctx; + + ptr = lexbor_str_append(ctx->str, ctx->mraw, data, len); + if (ptr == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_OK; +} + + +lxb_char_t * +lxb_css_syntax_token_serialize_char(const lxb_css_syntax_token_t *token, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_syntax_token_serialize(token, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_syntax_token_serialize(token, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_css_log_message_t * +lxb_css_syntax_token_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const char *module_name) +{ + lxb_char_t *name; + lxb_css_log_message_t *msg; + + static const char unexpected[] = "%s. Unexpected token: %s"; + + name = lxb_css_syntax_token_serialize_char(token, NULL); + if (name == NULL) { + return NULL; + } + + msg = lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, unexpected, + module_name, name); + + lexbor_free(name); + + return msg; +} + +/* + * No inline functions for ABI. + */ +lxb_css_syntax_token_t * +lxb_css_syntax_token_create_noi(lexbor_dobject_t *dobj) +{ + return lxb_css_syntax_token_create(dobj); +} + +void +lxb_css_syntax_token_clean_noi(lxb_css_syntax_token_t *token) +{ + lxb_css_syntax_token_clean(token); +} + +lxb_css_syntax_token_t * +lxb_css_syntax_token_destroy_noi(lxb_css_syntax_token_t *token, + lexbor_dobject_t *dobj) +{ + return lxb_css_syntax_token_destroy(token, dobj); +} + +const lxb_char_t * +lxb_css_syntax_token_type_name_noi(lxb_css_syntax_token_t *token) +{ + return lxb_css_syntax_token_type_name(token); +} + +lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_noi(lxb_css_syntax_token_t *token) +{ + return lxb_css_syntax_token_type(token); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/tokenizer.c b/ext/dom/lexbor/lexbor/css/syntax/tokenizer.c new file mode 100644 index 0000000000000..e8a8802e52983 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/tokenizer.c @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2018-2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/tokenizer.h" +#include "lexbor/css/syntax/tokenizer/error.h" +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/state_res.h" + +#include "lexbor/core/array.h" + +#define LEXBOR_STR_RES_MAP_LOWERCASE +#include "lexbor/core/str_res.h" + + +static const lxb_char_t lxb_css_syntax_tokenizer_important[] = "important"; + +static lxb_css_syntax_tokenizer_cache_t * +lxb_css_syntax_tokenizer_cache_create(void); + +static lxb_status_t +lxb_css_syntax_tokenizer_cache_init(lxb_css_syntax_tokenizer_cache_t *cache, + size_t size); + +static void +lxb_css_syntax_tokenizer_cache_clean(lxb_css_syntax_tokenizer_cache_t *cache); + +static lxb_css_syntax_tokenizer_cache_t * +lxb_css_syntax_tokenizer_cache_destroy(lxb_css_syntax_tokenizer_cache_t *cache); + +LXB_API lxb_status_t +lxb_css_syntax_tokenizer_cache_push(lxb_css_syntax_tokenizer_cache_t *cache, + lxb_css_syntax_token_t *value); + +static lxb_status_t +lxb_css_syntax_tokenizer_blank(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t **data, const lxb_char_t **end, + void *ctx); + +static bool +lxb_css_syntax_tokenizer_lookup_important_ch(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch, + lxb_css_syntax_token_type_t stop, + bool skip_first); + +static bool +lxb_css_syntax_tokenizer_lookup_important_end(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch, + lxb_css_syntax_token_type_t stop, + bool skip_first); + +static bool +lxb_css_syntax_tokenizer_lookup_important_tokens(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + bool skip_first); + + +lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_syntax_tokenizer_t)); +} + +lxb_status_t +lxb_css_syntax_tokenizer_init(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + static const unsigned tmp_size = 1024; + + if (tkz == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + /* Tokens. */ + + tkz->tokens = lexbor_dobject_create(); + status = lexbor_dobject_init(tkz->tokens, 128, + sizeof(lxb_css_syntax_token_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + /* Cache for Tokens. */ + + tkz->cache = lxb_css_syntax_tokenizer_cache_create(); + status = lxb_css_syntax_tokenizer_cache_init(tkz->cache, 128); + if (status != LXB_STATUS_OK) { + return status; + } + + /* Memory for text. */ + + tkz->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(tkz->mraw, 4096); + if (status != LXB_STATUS_OK) { + return status; + } + + /* Temp */ + tkz->start = lexbor_malloc(tmp_size); + if (tkz->start == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + tkz->pos = tkz->start; + tkz->end = tkz->start + tmp_size; + + /* Parse errors */ + tkz->parse_errors = lexbor_array_obj_create(); + status = lexbor_array_obj_init(tkz->parse_errors, 16, + sizeof(lxb_css_syntax_tokenizer_error_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + tkz->offset = 0; + tkz->cache_pos = 0; + tkz->prepared = 0; + + tkz->eof = false; + tkz->with_comment = false; + tkz->status = LXB_STATUS_OK; + tkz->opt = LXB_CSS_SYNTAX_TOKENIZER_OPT_UNDEF; + tkz->chunk_cb = lxb_css_syntax_tokenizer_blank; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_tokenizer_clean(lxb_css_syntax_tokenizer_t *tkz) +{ + lexbor_mraw_clean(tkz->mraw); + lexbor_array_obj_clean(tkz->parse_errors); + lxb_css_syntax_tokenizer_cache_clean(tkz->cache); + lexbor_dobject_clean(tkz->tokens); + + tkz->offset = 0; + tkz->cache_pos = 0; + tkz->prepared = 0; + + tkz->eof = false; + tkz->status = LXB_STATUS_OK; + tkz->in_begin = NULL; + tkz->in_end = NULL; + tkz->pos = tkz->start; + + return LXB_STATUS_OK; +} + +lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_destroy(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz == NULL) { + return NULL; + } + + if (tkz->tokens != NULL) { + tkz->tokens = lexbor_dobject_destroy(tkz->tokens, true); + tkz->cache = lxb_css_syntax_tokenizer_cache_destroy(tkz->cache); + } + + tkz->mraw = lexbor_mraw_destroy(tkz->mraw, true); + tkz->parse_errors = lexbor_array_obj_destroy(tkz->parse_errors, true); + + if (tkz->start != NULL) { + tkz->start = lexbor_free(tkz->start); + } + + return lexbor_free(tkz); +} + +static lxb_css_syntax_tokenizer_cache_t * +lxb_css_syntax_tokenizer_cache_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_syntax_tokenizer_cache_t)); +} + +static lxb_status_t +lxb_css_syntax_tokenizer_cache_init(lxb_css_syntax_tokenizer_cache_t *cache, + size_t size) +{ + cache->length = 0; + cache->size = size; + + cache->list = lexbor_malloc(sizeof(lxb_css_syntax_token_t *) * size); + if (cache->list == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_OK; +} + +static void +lxb_css_syntax_tokenizer_cache_clean(lxb_css_syntax_tokenizer_cache_t *cache) +{ + if (cache != NULL) { + cache->length = 0; + } +} + +static lxb_css_syntax_tokenizer_cache_t * +lxb_css_syntax_tokenizer_cache_destroy(lxb_css_syntax_tokenizer_cache_t *cache) +{ + if (cache == NULL) { + return NULL; + } + + if (cache->list) { + lexbor_free(cache->list); + } + + return lexbor_free(cache); +} + +static lxb_css_syntax_token_t ** +lxb_css_syntax_tokenizer_cache_expand(lxb_css_syntax_tokenizer_cache_t *cache, + size_t up_to) +{ + size_t new_size; + lxb_css_syntax_token_t **list; + + if (cache == NULL) { + return NULL; + } + + new_size = cache->length + up_to; + list = lexbor_realloc(cache->list, + sizeof(lxb_css_syntax_token_t *) * new_size); + if (cache == NULL) { + return NULL; + } + + cache->list = list; + cache->size = new_size; + + return list; +} + +lxb_status_t +lxb_css_syntax_tokenizer_cache_push(lxb_css_syntax_tokenizer_cache_t *cache, + lxb_css_syntax_token_t *value) +{ + if (cache->length >= cache->size) { + if ((lxb_css_syntax_tokenizer_cache_expand(cache, 128) == NULL)) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + cache->list[ cache->length ] = value; + cache->length++; + + return LXB_STATUS_OK; +} + + +static lxb_status_t +lxb_css_syntax_tokenizer_blank(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t **data, const lxb_char_t **end, + void *ctx) +{ + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_tokenizer_chunk(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, size_t size) +{ + return tkz->status; +} + +lxb_css_syntax_token_t * +lxb_css_syntax_tokenizer_token(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + lxb_css_syntax_token_t *token; + const lxb_char_t *begin, *end; + + begin = tkz->in_begin; + end = tkz->in_end; + + if (tkz->prepared != 0) { + if (tkz->cache_pos < tkz->prepared) { + token = tkz->cache->list[tkz->prepared - 1]; + + status = lxb_css_syntax_token_string_make(tkz, token); + if (status != LXB_STATUS_OK) { + return NULL; + } + } + + token = tkz->cache->list[tkz->prepared]; + token->offset = tkz->offset; + + tkz->prepared += 1; + + if (tkz->prepared >= tkz->cache->length) { + tkz->prepared = 0; + } + + if (lxb_css_syntax_token_base(token)->length != 0) { + tkz->offset += lxb_css_syntax_token_base(token)->length; + token->cloned = false; + return token; + } + + if (begin >= end) { + status = lxb_css_syntax_tokenizer_next_chunk(tkz, &begin, &end); + if (status != LXB_STATUS_OK) { + return NULL; + } + + if (begin >= end) { + lxb_css_syntax_token_base(token)->length = 1; + goto done; + } + } + + if (lxb_css_syntax_token_delim(token)->character == '-') { + begin = lxb_css_syntax_state_minus_process(tkz, token, begin, end); + } + else { + begin = lxb_css_syntax_state_plus_process(tkz, token, begin, end); + } + + goto done; + } + + if (tkz->cache_pos < tkz->cache->length) { + token = tkz->cache->list[tkz->cache->length - 1]; + + status = lxb_css_syntax_token_string_make(tkz, token); + if (status != LXB_STATUS_OK) { + return NULL; + } + } + + token = lxb_css_syntax_token_cached_create(tkz); + if (token == NULL) { + return NULL; + } + + token->offset = tkz->offset; + +again: + + if (begin >= end) { + status = lxb_css_syntax_tokenizer_next_chunk(tkz, &begin, &end); + if (status != LXB_STATUS_OK) { + return NULL; + } + + if (begin >= end) { + token->type = LXB_CSS_SYNTAX_TOKEN__EOF; + + lxb_css_syntax_token_base(token)->begin = begin; + lxb_css_syntax_token_base(token)->length = 0; + + token->cloned = false; + + return token; + } + } + + begin = lxb_css_syntax_state_res_map[*begin](tkz, token, begin, end); + +done: + + token->cloned = false; + + if (begin == NULL) { + return NULL; + } + + tkz->in_begin = begin; + tkz->offset += lxb_css_syntax_token_base(token)->length; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_COMMENT && !tkz->with_comment) { + end = tkz->in_end; + goto again; + } + + return token; +} + +lxb_status_t +lxb_css_syntax_tokenizer_next_chunk(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t **data, const lxb_char_t **end) +{ + const lxb_char_t *begin; + + if (tkz->eof == false) { + begin = *data; + + tkz->status = tkz->chunk_cb(tkz, data, end, tkz->chunk_ctx); + if (tkz->status != LXB_STATUS_OK) { + return tkz->status; + } + + if (*data >= *end) { + *data = begin; + *end = begin; + + tkz->in_begin = begin; + tkz->in_end = begin; + + tkz->eof = true; + } + else { + tkz->in_begin = *data; + tkz->in_end = *end; + } + } + + return LXB_STATUS_OK; +} + +bool +lxb_css_syntax_tokenizer_lookup_colon(lxb_css_syntax_tokenizer_t *tkz) +{ + const lxb_char_t *p, *end; + lxb_css_syntax_token_t *token; + + if (tkz->cache_pos + 1 < tkz->cache->length) { + token = tkz->cache->list[tkz->cache_pos + 1]; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + if (tkz->cache_pos + 2 < tkz->cache->length) { + token = tkz->cache->list[tkz->cache_pos + 2]; + + return token->type == LXB_CSS_SYNTAX_TOKEN_COLON; + } + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + return true; + } + + return false; + } + + p = tkz->in_begin; + end = tkz->in_end; + + do { + if (p >= end) { + token = lxb_css_syntax_token_next(tkz); + if (token == NULL) { + return false; + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + token = lxb_css_syntax_token_next(tkz); + if (token == NULL) { + return false; + } + } + + return token->type == LXB_CSS_SYNTAX_TOKEN_COLON; + } + + switch (*p) { + case 0x3A: + return true; + + case 0x0D: + case 0x0C: + case 0x09: + case 0x20: + case 0x0A: + p++; + break; + + default: + return false; + } + } + while (true); +} + +bool +lxb_css_syntax_tokenizer_lookup_important(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch) +{ + const lxb_char_t *p, *end; + lxb_css_syntax_token_t *token; + + static const size_t length = sizeof(lxb_css_syntax_tokenizer_important) - 1; + + p = tkz->in_begin; + end = tkz->in_end; + + if (tkz->cache_pos + 1 < tkz->cache->length) { + token = tkz->cache->list[tkz->cache_pos + 1]; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + if (!(lxb_css_syntax_token_ident(token)->length == length + && lexbor_str_data_ncasecmp(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_tokenizer_important, + length))) + { + return false; + } + + if (tkz->cache_pos + 2 < tkz->cache->length) { + token = tkz->cache->list[tkz->cache_pos + 2]; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + if (tkz->cache_pos + 3 >= tkz->cache->length) { + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, + p, end, stop_ch, stop, false); + } + + token = tkz->cache->list[tkz->cache_pos + 3]; + } + + return (token->type == LXB_CSS_SYNTAX_TOKEN_SEMICOLON + || token->type == stop + || token->type == LXB_CSS_SYNTAX_TOKEN__EOF); + } + + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, p, end, + stop_ch, stop, false); + } + + return lxb_css_syntax_tokenizer_lookup_important_ch(tkz, p, end, stop_ch, + stop, false); +} + +static bool +lxb_css_syntax_tokenizer_lookup_important_ch(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch, + lxb_css_syntax_token_type_t stop, + bool skip_first) +{ + const lxb_char_t *imp; + + imp = lxb_css_syntax_tokenizer_important; + + do { + if (p >= end) { + return lxb_css_syntax_tokenizer_lookup_important_tokens(tkz, stop, + skip_first); + } + + if (lexbor_str_res_map_lowercase[*p++] != *imp++) { + return false; + } + } + while (*imp != 0x00); + + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, p, end, stop_ch, + stop, skip_first); +} + +static bool +lxb_css_syntax_tokenizer_lookup_important_end(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch, + lxb_css_syntax_token_type_t stop, + bool skip_first) +{ + do { + if (p >= end) { + return lxb_css_syntax_tokenizer_lookup_important_tokens(tkz, stop, + skip_first); + } + + switch (*p) { + case 0x3B: + return true; + + case 0x0D: + case 0x0C: + case 0x09: + case 0x20: + case 0x0A: + p++; + break; + + default: + return (stop_ch != 0x00 && stop_ch == *p); + } + } + while (true); +} + +static bool +lxb_css_syntax_tokenizer_lookup_important_tokens(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + bool skip_first) +{ + const lxb_css_syntax_token_t *next; + + static const size_t length = sizeof(lxb_css_syntax_tokenizer_important) - 1; + + if (skip_first) { + next = lxb_css_syntax_token_next(tkz); + if (next == NULL) { + return false; + } + } + + next = lxb_css_syntax_token_next(tkz); + if (next == NULL) { + return false; + } + + if (next->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + if (!(lxb_css_syntax_token_ident(next)->length == length + && lexbor_str_data_ncasecmp(lxb_css_syntax_token_ident(next)->data, + lxb_css_syntax_tokenizer_important, + length))) + { + return false; + } + + next = lxb_css_syntax_token_next(tkz); + if (next == NULL) { + return false; + } + + if (next->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + next = lxb_css_syntax_token_next(tkz); + if (next == NULL) { + return false; + } + } + + return (next->type == LXB_CSS_SYNTAX_TOKEN_SEMICOLON + || next->type == stop || next->type == LXB_CSS_SYNTAX_TOKEN__EOF); +} + +bool +lxb_css_syntax_tokenizer_lookup_declaration_ws_end(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch) +{ + lxb_css_syntax_token_t *token; + const lxb_char_t *p, *end; + + if (tkz->cache_pos + 1 < tkz->cache->length) { + token = tkz->cache->list[tkz->cache_pos + 1]; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '!') { + return lxb_css_syntax_tokenizer_lookup_important(tkz, stop, + stop_ch); + } + + return false; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return true; + + default: + return token->type == stop_ch || + token->type == LXB_CSS_SYNTAX_TOKEN__EOF; + } + } + + p = tkz->in_begin; + end = tkz->in_end; + + do { + if (p >= end) { + return lxb_css_syntax_tokenizer_lookup_important_tokens(tkz, stop, + true); + } + + switch (*p) { + case 0x3B: + return true; + + case 0x21: + p++; + return lxb_css_syntax_tokenizer_lookup_important_ch(tkz, p, end, + stop_ch, stop, true); + + default: + return (stop_ch != 0x00 && stop_ch == *p); + } + } + while (true); +} + +/* + * No inline functions for ABI. + */ +lxb_status_t +lxb_css_syntax_tokenizer_status_noi(lxb_css_syntax_tokenizer_t *tkz) +{ + return lxb_css_syntax_tokenizer_status(tkz); +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/tokenizer.h b/ext/dom/lexbor/lexbor/css/syntax/tokenizer.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/css/syntax/tokenizer/error.c b/ext/dom/lexbor/lexbor/css/syntax/tokenizer/error.c new file mode 100644 index 0000000000000..a95723cb6da48 --- /dev/null +++ b/ext/dom/lexbor/lexbor/css/syntax/tokenizer/error.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018-2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/tokenizer/error.h" + + +lxb_css_syntax_tokenizer_error_t * +lxb_css_syntax_tokenizer_error_add(lexbor_array_obj_t *parse_errors, + const lxb_char_t *pos, + lxb_css_syntax_tokenizer_error_id_t id) +{ + if (parse_errors == NULL) { + return NULL; + } + + lxb_css_syntax_tokenizer_error_t *entry; + + entry = lexbor_array_obj_push(parse_errors); + if (entry == NULL) { + return NULL; + } + + entry->id = id; + entry->pos = pos; + + return entry; +} diff --git a/ext/dom/lexbor/lexbor/css/syntax/tokenizer/error.h b/ext/dom/lexbor/lexbor/css/syntax/tokenizer/error.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/base.h b/ext/dom/lexbor/lexbor/dom/base.h index b11bddb1e64fd..414be6a08a4c1 100644 --- a/ext/dom/lexbor/lexbor/dom/base.h +++ b/ext/dom/lexbor/lexbor/dom/base.h @@ -16,7 +16,7 @@ extern "C" { #define LXB_DOM_VERSION_MAJOR 1 -#define LXB_DOM_VERSION_MINOR 6 +#define LXB_DOM_VERSION_MINOR 7 #define LXB_DOM_VERSION_PATCH 0 #define LXB_DOM_VERSION_STRING \ diff --git a/ext/dom/lexbor/lexbor/dom/interface.h b/ext/dom/lexbor/lexbor/dom/interface.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/cdata_section.c b/ext/dom/lexbor/lexbor/dom/interfaces/cdata_section.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/cdata_section.h b/ext/dom/lexbor/lexbor/dom/interfaces/cdata_section.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/character_data.c b/ext/dom/lexbor/lexbor/dom/interfaces/character_data.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/character_data.h b/ext/dom/lexbor/lexbor/dom/interfaces/character_data.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/comment.c b/ext/dom/lexbor/lexbor/dom/interfaces/comment.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/comment.h b/ext/dom/lexbor/lexbor/dom/interfaces/comment.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document.c b/ext/dom/lexbor/lexbor/dom/interfaces/document.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document.h b/ext/dom/lexbor/lexbor/dom/interfaces/document.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document_fragment.c b/ext/dom/lexbor/lexbor/dom/interfaces/document_fragment.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document_fragment.h b/ext/dom/lexbor/lexbor/dom/interfaces/document_fragment.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document_type.c b/ext/dom/lexbor/lexbor/dom/interfaces/document_type.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/document_type.h b/ext/dom/lexbor/lexbor/dom/interfaces/document_type.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/element.c b/ext/dom/lexbor/lexbor/dom/interfaces/element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/element.h b/ext/dom/lexbor/lexbor/dom/interfaces/element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/event_target.c b/ext/dom/lexbor/lexbor/dom/interfaces/event_target.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/event_target.h b/ext/dom/lexbor/lexbor/dom/interfaces/event_target.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/node.c b/ext/dom/lexbor/lexbor/dom/interfaces/node.c old mode 100755 new mode 100644 index 4d1bc9493c209..702d1a802c16d --- a/ext/dom/lexbor/lexbor/dom/interfaces/node.c +++ b/ext/dom/lexbor/lexbor/dom/interfaces/node.c @@ -1239,7 +1239,7 @@ lxb_dom_node_text_content_set(lxb_dom_node_t *node, } bool -lxb_dom_node_is_empty(lxb_dom_node_t *root) +lxb_dom_node_is_empty(const lxb_dom_node_t *root) { lxb_char_t chr; lexbor_str_t *str; diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/node.h b/ext/dom/lexbor/lexbor/dom/interfaces/node.h old mode 100755 new mode 100644 index ff9c9246f940a..8ac218b4cabd8 --- a/ext/dom/lexbor/lexbor/dom/interfaces/node.h +++ b/ext/dom/lexbor/lexbor/dom/interfaces/node.h @@ -166,7 +166,7 @@ lxb_dom_node_text_content_set(lxb_dom_node_t *node, const lxb_char_t *content, size_t len); LXB_API bool -lxb_dom_node_is_empty(lxb_dom_node_t *root); +lxb_dom_node_is_empty(const lxb_dom_node_t *root); /* diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/processing_instruction.c b/ext/dom/lexbor/lexbor/dom/interfaces/processing_instruction.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/processing_instruction.h b/ext/dom/lexbor/lexbor/dom/interfaces/processing_instruction.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/shadow_root.c b/ext/dom/lexbor/lexbor/dom/interfaces/shadow_root.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/shadow_root.h b/ext/dom/lexbor/lexbor/dom/interfaces/shadow_root.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/text.c b/ext/dom/lexbor/lexbor/dom/interfaces/text.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/dom/interfaces/text.h b/ext/dom/lexbor/lexbor/dom/interfaces/text.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/base.h b/ext/dom/lexbor/lexbor/html/base.h old mode 100755 new mode 100644 index 66bac9b3c2ad3..2423857ae3327 --- a/ext/dom/lexbor/lexbor/html/base.h +++ b/ext/dom/lexbor/lexbor/html/base.h @@ -15,7 +15,7 @@ extern "C" { #define LXB_HTML_VERSION_MAJOR 2 -#define LXB_HTML_VERSION_MINOR 3 +#define LXB_HTML_VERSION_MINOR 5 #define LXB_HTML_VERSION_PATCH 0 #define LXB_HTML_VERSION_STRING LEXBOR_STRINGIZE(LXB_HTML_VERSION_MAJOR) "." \ diff --git a/ext/dom/lexbor/lexbor/html/interface.c b/ext/dom/lexbor/lexbor/html/interface.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interface.h b/ext/dom/lexbor/lexbor/html/interface.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/anchor_element.c b/ext/dom/lexbor/lexbor/html/interfaces/anchor_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/anchor_element.h b/ext/dom/lexbor/lexbor/html/interfaces/anchor_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/area_element.c b/ext/dom/lexbor/lexbor/html/interfaces/area_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/area_element.h b/ext/dom/lexbor/lexbor/html/interfaces/area_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/audio_element.c b/ext/dom/lexbor/lexbor/html/interfaces/audio_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/audio_element.h b/ext/dom/lexbor/lexbor/html/interfaces/audio_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/base_element.c b/ext/dom/lexbor/lexbor/html/interfaces/base_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/base_element.h b/ext/dom/lexbor/lexbor/html/interfaces/base_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/body_element.c b/ext/dom/lexbor/lexbor/html/interfaces/body_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/body_element.h b/ext/dom/lexbor/lexbor/html/interfaces/body_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/br_element.c b/ext/dom/lexbor/lexbor/html/interfaces/br_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/br_element.h b/ext/dom/lexbor/lexbor/html/interfaces/br_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/button_element.c b/ext/dom/lexbor/lexbor/html/interfaces/button_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/button_element.h b/ext/dom/lexbor/lexbor/html/interfaces/button_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/canvas_element.c b/ext/dom/lexbor/lexbor/html/interfaces/canvas_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/canvas_element.h b/ext/dom/lexbor/lexbor/html/interfaces/canvas_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/d_list_element.c b/ext/dom/lexbor/lexbor/html/interfaces/d_list_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/d_list_element.h b/ext/dom/lexbor/lexbor/html/interfaces/d_list_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/data_element.c b/ext/dom/lexbor/lexbor/html/interfaces/data_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/data_element.h b/ext/dom/lexbor/lexbor/html/interfaces/data_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/data_list_element.c b/ext/dom/lexbor/lexbor/html/interfaces/data_list_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/data_list_element.h b/ext/dom/lexbor/lexbor/html/interfaces/data_list_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/details_element.c b/ext/dom/lexbor/lexbor/html/interfaces/details_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/details_element.h b/ext/dom/lexbor/lexbor/html/interfaces/details_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/dialog_element.c b/ext/dom/lexbor/lexbor/html/interfaces/dialog_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/dialog_element.h b/ext/dom/lexbor/lexbor/html/interfaces/dialog_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/directory_element.c b/ext/dom/lexbor/lexbor/html/interfaces/directory_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/directory_element.h b/ext/dom/lexbor/lexbor/html/interfaces/directory_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/div_element.c b/ext/dom/lexbor/lexbor/html/interfaces/div_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/div_element.h b/ext/dom/lexbor/lexbor/html/interfaces/div_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/document.c b/ext/dom/lexbor/lexbor/html/interfaces/document.c old mode 100755 new mode 100644 index b4de5b8514a83..aa305a920f998 --- a/ext/dom/lexbor/lexbor/html/interfaces/document.c +++ b/ext/dom/lexbor/lexbor/html/interfaces/document.c @@ -58,6 +58,7 @@ static lxb_html_document_css_custom_entry_t * lxb_html_document_css_customs_insert(lxb_html_document_t *document, const lxb_char_t *key, size_t length); +#if 0 static lxb_status_t lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, @@ -72,7 +73,6 @@ static lxb_status_t lxb_html_document_style_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx); -#if 0 static lxb_status_t lxb_html_document_done(lxb_html_document_t *document); #endif @@ -604,23 +604,30 @@ lxb_status_t lxb_html_document_style_attach(lxb_html_document_t *document, lxb_css_rule_style_t *style) { +#if 0 lxb_html_document_css_t *css = &document->css; return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), style->selector, lxb_html_document_style_cb, style); +#endif + return LXB_STATUS_OK; } lxb_status_t lxb_html_document_style_remove(lxb_html_document_t *document, lxb_css_rule_style_t *style) { +#if 0 lxb_html_document_css_t *css = &document->css; return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), style->selector, lxb_html_document_style_remove_by_rule_cb, style); +#endif + return LXB_STATUS_OK; } +#if 0 static lxb_status_t lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, @@ -658,18 +665,25 @@ lxb_html_document_style_remove_avl_cb(lexbor_avl_t *avl, style, context->list); return LXB_STATUS_OK; } +#endif lxb_status_t lxb_html_document_style_attach_by_element(lxb_html_document_t *document, lxb_html_element_t *element, lxb_css_rule_style_t *style) { +#if 0 lxb_html_document_css_t *css = &document->css; - return lxb_selectors_find_reverse(css->selectors, lxb_dom_interface_node(element), - style->selector, lxb_html_document_style_cb, style); + return lxb_selectors_match_node(css->selectors, + lxb_dom_interface_node(element), + style->selector, + lxb_html_document_style_cb, style); +#endif + return LXB_STATUS_OK; } +#if 0 static lxb_status_t lxb_html_document_style_cb(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, void *ctx) @@ -685,6 +699,7 @@ lxb_html_document_style_cb(lxb_dom_node_t *node, return lxb_html_element_style_list_append(lxb_html_interface_element(node), style->declarations, spec); } +#endif lxb_html_document_t * lxb_html_document_destroy(lxb_html_document_t *document) diff --git a/ext/dom/lexbor/lexbor/html/interfaces/document.h b/ext/dom/lexbor/lexbor/html/interfaces/document.h old mode 100755 new mode 100644 index 7e8d1ea2e5644..827ff64f50f96 --- a/ext/dom/lexbor/lexbor/html/interfaces/document.h +++ b/ext/dom/lexbor/lexbor/html/interfaces/document.h @@ -20,7 +20,6 @@ extern "C" { #include "lexbor/dom/interfaces/attr.h" #include "lexbor/dom/interfaces/document.h" #include "lexbor/css/css.h" -#include "lexbor/selectors/selectors.h" typedef lxb_status_t @@ -45,7 +44,6 @@ typedef struct { lxb_css_memory_t *memory; lxb_css_selectors_t *css_selectors; lxb_css_parser_t *parser; - lxb_selectors_t *selectors; lexbor_avl_t *styles; lexbor_array_t *stylesheets; diff --git a/ext/dom/lexbor/lexbor/html/interfaces/element.c b/ext/dom/lexbor/lexbor/html/interfaces/element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/element.h b/ext/dom/lexbor/lexbor/html/interfaces/element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/embed_element.c b/ext/dom/lexbor/lexbor/html/interfaces/embed_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/embed_element.h b/ext/dom/lexbor/lexbor/html/interfaces/embed_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/field_set_element.c b/ext/dom/lexbor/lexbor/html/interfaces/field_set_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/field_set_element.h b/ext/dom/lexbor/lexbor/html/interfaces/field_set_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/font_element.c b/ext/dom/lexbor/lexbor/html/interfaces/font_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/font_element.h b/ext/dom/lexbor/lexbor/html/interfaces/font_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/form_element.c b/ext/dom/lexbor/lexbor/html/interfaces/form_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/form_element.h b/ext/dom/lexbor/lexbor/html/interfaces/form_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/frame_element.c b/ext/dom/lexbor/lexbor/html/interfaces/frame_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/frame_element.h b/ext/dom/lexbor/lexbor/html/interfaces/frame_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/frame_set_element.c b/ext/dom/lexbor/lexbor/html/interfaces/frame_set_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/frame_set_element.h b/ext/dom/lexbor/lexbor/html/interfaces/frame_set_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/head_element.c b/ext/dom/lexbor/lexbor/html/interfaces/head_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/head_element.h b/ext/dom/lexbor/lexbor/html/interfaces/head_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/heading_element.c b/ext/dom/lexbor/lexbor/html/interfaces/heading_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/heading_element.h b/ext/dom/lexbor/lexbor/html/interfaces/heading_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/hr_element.c b/ext/dom/lexbor/lexbor/html/interfaces/hr_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/hr_element.h b/ext/dom/lexbor/lexbor/html/interfaces/hr_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/html_element.c b/ext/dom/lexbor/lexbor/html/interfaces/html_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/html_element.h b/ext/dom/lexbor/lexbor/html/interfaces/html_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/iframe_element.c b/ext/dom/lexbor/lexbor/html/interfaces/iframe_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/iframe_element.h b/ext/dom/lexbor/lexbor/html/interfaces/iframe_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/image_element.c b/ext/dom/lexbor/lexbor/html/interfaces/image_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/image_element.h b/ext/dom/lexbor/lexbor/html/interfaces/image_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/input_element.c b/ext/dom/lexbor/lexbor/html/interfaces/input_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/input_element.h b/ext/dom/lexbor/lexbor/html/interfaces/input_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/label_element.c b/ext/dom/lexbor/lexbor/html/interfaces/label_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/label_element.h b/ext/dom/lexbor/lexbor/html/interfaces/label_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/legend_element.c b/ext/dom/lexbor/lexbor/html/interfaces/legend_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/legend_element.h b/ext/dom/lexbor/lexbor/html/interfaces/legend_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/li_element.c b/ext/dom/lexbor/lexbor/html/interfaces/li_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/li_element.h b/ext/dom/lexbor/lexbor/html/interfaces/li_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/link_element.c b/ext/dom/lexbor/lexbor/html/interfaces/link_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/link_element.h b/ext/dom/lexbor/lexbor/html/interfaces/link_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/map_element.c b/ext/dom/lexbor/lexbor/html/interfaces/map_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/map_element.h b/ext/dom/lexbor/lexbor/html/interfaces/map_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/marquee_element.c b/ext/dom/lexbor/lexbor/html/interfaces/marquee_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/marquee_element.h b/ext/dom/lexbor/lexbor/html/interfaces/marquee_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/media_element.c b/ext/dom/lexbor/lexbor/html/interfaces/media_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/media_element.h b/ext/dom/lexbor/lexbor/html/interfaces/media_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/menu_element.c b/ext/dom/lexbor/lexbor/html/interfaces/menu_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/menu_element.h b/ext/dom/lexbor/lexbor/html/interfaces/menu_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/meta_element.c b/ext/dom/lexbor/lexbor/html/interfaces/meta_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/meta_element.h b/ext/dom/lexbor/lexbor/html/interfaces/meta_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/meter_element.c b/ext/dom/lexbor/lexbor/html/interfaces/meter_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/meter_element.h b/ext/dom/lexbor/lexbor/html/interfaces/meter_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/mod_element.c b/ext/dom/lexbor/lexbor/html/interfaces/mod_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/mod_element.h b/ext/dom/lexbor/lexbor/html/interfaces/mod_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/o_list_element.c b/ext/dom/lexbor/lexbor/html/interfaces/o_list_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/o_list_element.h b/ext/dom/lexbor/lexbor/html/interfaces/o_list_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/object_element.c b/ext/dom/lexbor/lexbor/html/interfaces/object_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/object_element.h b/ext/dom/lexbor/lexbor/html/interfaces/object_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/opt_group_element.c b/ext/dom/lexbor/lexbor/html/interfaces/opt_group_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/opt_group_element.h b/ext/dom/lexbor/lexbor/html/interfaces/opt_group_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/option_element.c b/ext/dom/lexbor/lexbor/html/interfaces/option_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/option_element.h b/ext/dom/lexbor/lexbor/html/interfaces/option_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/output_element.c b/ext/dom/lexbor/lexbor/html/interfaces/output_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/output_element.h b/ext/dom/lexbor/lexbor/html/interfaces/output_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/paragraph_element.c b/ext/dom/lexbor/lexbor/html/interfaces/paragraph_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/paragraph_element.h b/ext/dom/lexbor/lexbor/html/interfaces/paragraph_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/param_element.c b/ext/dom/lexbor/lexbor/html/interfaces/param_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/param_element.h b/ext/dom/lexbor/lexbor/html/interfaces/param_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/picture_element.c b/ext/dom/lexbor/lexbor/html/interfaces/picture_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/picture_element.h b/ext/dom/lexbor/lexbor/html/interfaces/picture_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/pre_element.c b/ext/dom/lexbor/lexbor/html/interfaces/pre_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/pre_element.h b/ext/dom/lexbor/lexbor/html/interfaces/pre_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/progress_element.c b/ext/dom/lexbor/lexbor/html/interfaces/progress_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/progress_element.h b/ext/dom/lexbor/lexbor/html/interfaces/progress_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/quote_element.c b/ext/dom/lexbor/lexbor/html/interfaces/quote_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/quote_element.h b/ext/dom/lexbor/lexbor/html/interfaces/quote_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/script_element.c b/ext/dom/lexbor/lexbor/html/interfaces/script_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/script_element.h b/ext/dom/lexbor/lexbor/html/interfaces/script_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/select_element.c b/ext/dom/lexbor/lexbor/html/interfaces/select_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/select_element.h b/ext/dom/lexbor/lexbor/html/interfaces/select_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/slot_element.c b/ext/dom/lexbor/lexbor/html/interfaces/slot_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/slot_element.h b/ext/dom/lexbor/lexbor/html/interfaces/slot_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/source_element.c b/ext/dom/lexbor/lexbor/html/interfaces/source_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/source_element.h b/ext/dom/lexbor/lexbor/html/interfaces/source_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/span_element.c b/ext/dom/lexbor/lexbor/html/interfaces/span_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/span_element.h b/ext/dom/lexbor/lexbor/html/interfaces/span_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/style_element.c b/ext/dom/lexbor/lexbor/html/interfaces/style_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/style_element.h b/ext/dom/lexbor/lexbor/html/interfaces/style_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_caption_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_caption_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_caption_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_caption_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_cell_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_cell_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_cell_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_cell_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_col_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_col_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_col_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_col_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_row_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_row_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_row_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_row_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_section_element.c b/ext/dom/lexbor/lexbor/html/interfaces/table_section_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/table_section_element.h b/ext/dom/lexbor/lexbor/html/interfaces/table_section_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/template_element.c b/ext/dom/lexbor/lexbor/html/interfaces/template_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/template_element.h b/ext/dom/lexbor/lexbor/html/interfaces/template_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/text_area_element.c b/ext/dom/lexbor/lexbor/html/interfaces/text_area_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/text_area_element.h b/ext/dom/lexbor/lexbor/html/interfaces/text_area_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/time_element.c b/ext/dom/lexbor/lexbor/html/interfaces/time_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/time_element.h b/ext/dom/lexbor/lexbor/html/interfaces/time_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/title_element.c b/ext/dom/lexbor/lexbor/html/interfaces/title_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/title_element.h b/ext/dom/lexbor/lexbor/html/interfaces/title_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/track_element.c b/ext/dom/lexbor/lexbor/html/interfaces/track_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/track_element.h b/ext/dom/lexbor/lexbor/html/interfaces/track_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/u_list_element.c b/ext/dom/lexbor/lexbor/html/interfaces/u_list_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/u_list_element.h b/ext/dom/lexbor/lexbor/html/interfaces/u_list_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/unknown_element.c b/ext/dom/lexbor/lexbor/html/interfaces/unknown_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/unknown_element.h b/ext/dom/lexbor/lexbor/html/interfaces/unknown_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/video_element.c b/ext/dom/lexbor/lexbor/html/interfaces/video_element.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/video_element.h b/ext/dom/lexbor/lexbor/html/interfaces/video_element.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/window.c b/ext/dom/lexbor/lexbor/html/interfaces/window.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/interfaces/window.h b/ext/dom/lexbor/lexbor/html/interfaces/window.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/parser.c b/ext/dom/lexbor/lexbor/html/parser.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/parser.h b/ext/dom/lexbor/lexbor/html/parser.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/serialize.h b/ext/dom/lexbor/lexbor/html/serialize.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tag.h b/ext/dom/lexbor/lexbor/html/tag.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/token.c b/ext/dom/lexbor/lexbor/html/token.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/token.h b/ext/dom/lexbor/lexbor/html/token.h old mode 100755 new mode 100644 index eda8abcf5b994..0b7f4fdd04e3c --- a/ext/dom/lexbor/lexbor/html/token.h +++ b/ext/dom/lexbor/lexbor/html/token.h @@ -33,8 +33,8 @@ enum lxb_html_token_type { typedef struct { const lxb_char_t *begin; const lxb_char_t *end; - size_t line; - size_t column; + size_t line; + size_t column; const lxb_char_t *text_start; const lxb_char_t *text_end; diff --git a/ext/dom/lexbor/lexbor/html/token_attr.c b/ext/dom/lexbor/lexbor/html/token_attr.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/token_attr.h b/ext/dom/lexbor/lexbor/html/token_attr.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer.c b/ext/dom/lexbor/lexbor/html/tokenizer.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer.h b/ext/dom/lexbor/lexbor/html/tokenizer.h old mode 100755 new mode 100644 index 0be1c73408520..74bb55ef0fd78 --- a/ext/dom/lexbor/lexbor/html/tokenizer.h +++ b/ext/dom/lexbor/lexbor/html/tokenizer.h @@ -73,8 +73,8 @@ struct lxb_html_tokenizer { const lxb_char_t *end; const lxb_char_t *begin; const lxb_char_t *last; - size_t current_line; - size_t current_column; + size_t current_line; + size_t current_column; /* Entities */ const lexbor_sbst_entry_static_t *entity; diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/error.c b/ext/dom/lexbor/lexbor/html/tokenizer/error.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/error.h b/ext/dom/lexbor/lexbor/html/tokenizer/error.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/res.h b/ext/dom/lexbor/lexbor/html/tokenizer/res.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state.c b/ext/dom/lexbor/lexbor/html/tokenizer/state.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state.h b/ext/dom/lexbor/lexbor/html/tokenizer/state.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_comment.c b/ext/dom/lexbor/lexbor/html/tokenizer/state_comment.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_comment.h b/ext/dom/lexbor/lexbor/html/tokenizer/state_comment.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_doctype.c b/ext/dom/lexbor/lexbor/html/tokenizer/state_doctype.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_doctype.h b/ext/dom/lexbor/lexbor/html/tokenizer/state_doctype.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_rawtext.c b/ext/dom/lexbor/lexbor/html/tokenizer/state_rawtext.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_rawtext.h b/ext/dom/lexbor/lexbor/html/tokenizer/state_rawtext.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_rcdata.c b/ext/dom/lexbor/lexbor/html/tokenizer/state_rcdata.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_rcdata.h b/ext/dom/lexbor/lexbor/html/tokenizer/state_rcdata.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_script.c b/ext/dom/lexbor/lexbor/html/tokenizer/state_script.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tokenizer/state_script.h b/ext/dom/lexbor/lexbor/html/tokenizer/state_script.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree.c b/ext/dom/lexbor/lexbor/html/tree.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree.h b/ext/dom/lexbor/lexbor/html/tree.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/active_formatting.c b/ext/dom/lexbor/lexbor/html/tree/active_formatting.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/active_formatting.h b/ext/dom/lexbor/lexbor/html/tree/active_formatting.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/error.c b/ext/dom/lexbor/lexbor/html/tree/error.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/error.h b/ext/dom/lexbor/lexbor/html/tree/error.h old mode 100755 new mode 100644 index 685dd400d4033..ed1859f355a67 --- a/ext/dom/lexbor/lexbor/html/tree/error.h +++ b/ext/dom/lexbor/lexbor/html/tree/error.h @@ -97,9 +97,9 @@ lxb_html_tree_error_id_t; typedef struct { lxb_html_tree_error_id_t id; - size_t line; - size_t column; - size_t length; + size_t line; + size_t column; + size_t length; } lxb_html_tree_error_t; diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode.h b/ext/dom/lexbor/lexbor/html/tree/insertion_mode.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_after_body.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_after_body.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_after_frameset.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_after_frameset.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_body.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_body.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_frameset.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_frameset.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_head.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/after_head.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/before_head.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/before_head.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/before_html.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/before_html.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/foreign_content.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/foreign_content.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_body.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_body.c old mode 100755 new mode 100644 index 2f160543cd16d..32cc40476948f --- a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_body.c +++ b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_body.c @@ -1320,8 +1320,6 @@ lxb_html_tree_insertion_mode_in_body_textarea(lxb_html_tree_t *tree, lxb_html_tokenizer_state_set(tree->tkz_ref, lxb_html_tokenizer_state_rcdata_before); - tree->original_mode = tree->mode; - tree->frameset_ok = false; tree->original_mode = tree->mode; diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_caption.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_caption.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_cell.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_cell.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_column_group.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_column_group.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_frameset.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_frameset.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_head.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_head.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_head_noscript.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_head_noscript.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_row.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_row.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_select.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_select.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_select_in_table.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_select_in_table.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table_body.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table_body.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table_text.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_table_text.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_template.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/in_template.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/initial.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/initial.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/insertion_mode/text.c b/ext/dom/lexbor/lexbor/html/tree/insertion_mode/text.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/open_elements.c b/ext/dom/lexbor/lexbor/html/tree/open_elements.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/open_elements.h b/ext/dom/lexbor/lexbor/html/tree/open_elements.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/template_insertion.c b/ext/dom/lexbor/lexbor/html/tree/template_insertion.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree/template_insertion.h b/ext/dom/lexbor/lexbor/html/tree/template_insertion.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/html/tree_res.h b/ext/dom/lexbor/lexbor/html/tree_res.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/ns/ns.c b/ext/dom/lexbor/lexbor/ns/ns.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/ns/ns.h b/ext/dom/lexbor/lexbor/ns/ns.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/ports/posix/lexbor/core/memory.c b/ext/dom/lexbor/lexbor/ports/posix/lexbor/core/memory.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/ports/windows_nt/lexbor/core/memory.c b/ext/dom/lexbor/lexbor/ports/windows_nt/lexbor/core/memory.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c new file mode 100644 index 0000000000000..94d2c9386fbdc --- /dev/null +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c @@ -0,0 +1,1929 @@ +/* + * Copyright (C) 2021-2024 Alexander Borisov + * + * Author: Alexander Borisov + * Adapted for PHP + libxml2 by: Niels Dossche + * Based on Lexbor 2.4.0 (upstream commit d190520f9be4a9076411c70319df7de8318d8241) + */ + +#include +#include +#include +#include +#include +#include + +#include "lexbor/selectors-adapted/selectors.h" +#include "ext/dom/namespace_compat.h" +#include "ext/dom/domexception.h" + +#include + +/* Note: casting and then comparing is a bit faster on my i7-4790 */ +#define CMP_NODE_TYPE(node, ty) ((unsigned char) (node)->type == ty) + +static zend_always_inline bool lxb_selectors_adapted_is_matchable_child(const xmlNode *node) +{ + return CMP_NODE_TYPE(node, XML_ELEMENT_NODE); +} + +static zend_always_inline bool lxb_selectors_adapted_cmp_local_name_literal(const xmlNode *node, const char *name) +{ + return strcmp((const char *) node->name, name) == 0; +} + +static zend_always_inline bool lxb_selectors_adapted_cmp_ns(const xmlNode *a, const xmlNode *b) +{ + /* Namespace URIs are not interned, hence a->href != b->href. */ + return a->ns == b->ns || (a->ns != NULL && b->ns != NULL && xmlStrEqual(a->ns->href, b->ns->href)); +} + +static zend_always_inline bool lxb_selectors_adapted_cmp_local_name_id(const xmlNode *node, const lxb_selectors_adapted_id *id) +{ + uintptr_t ptr = (uintptr_t) node->name; + if ((ptr & (ZEND_MM_ALIGNMENT - 1)) != 0) { + /* It cannot be a heap-allocated string because the pointer is not properly aligned for a heap allocation. + * Therefore, it must be interned into the dictionary pool. */ + return node->name == id->name; + } + + return strcmp((const char *) node->name, (const char *) id->name) == 0; +} + +static zend_always_inline const xmlAttr *lxb_selectors_adapted_attr(const xmlNode *node, const lxb_char_t *name) +{ + const xmlAttr *attr = xmlHasProp(node, (const xmlChar *) name); + if (attr != NULL && attr->ns != NULL) { + return NULL; + } + return attr; +} + +static zend_always_inline bool lxb_selectors_adapted_has_attr(const xmlNode *node, const char *name) +{ + return lxb_selectors_adapted_attr(node, (const lxb_char_t *) name) != NULL; +} + +static zend_always_inline const lxb_char_t *lxb_selectors_adapted_attr_value_raw(const xmlAttr *attr) +{ + ZEND_ASSERT(attr != NULL); + + if (attr->children == NULL) { + return NULL; + } + + return (const lxb_char_t *) attr->children->content; +} + +static zend_always_inline lexbor_str_t lxb_selectors_adapted_attr_value_nullable(const xmlAttr *attr) +{ + lexbor_str_t ret; + ret.data = (lxb_char_t *) lxb_selectors_adapted_attr_value_raw(attr); + ret.length = (ret.data == NULL) ? 0 : strlen((const char *) ret.data); + return ret; +} + +static zend_always_inline lexbor_str_t lxb_selectors_adapted_attr_value_empty(const xmlAttr *attr) +{ + lexbor_str_t ret; + ret.data = (lxb_char_t *) lxb_selectors_adapted_attr_value_raw(attr); + if (ret.data == NULL) { + ret.data = (lxb_char_t *) ""; + ret.length = 0; + } else { + ret.length = strlen((const char *) ret.data); + } + return ret; +} + +static void lxb_selectors_adapted_set_entry_id_ex(lxb_selectors_entry_t *entry, const lxb_css_selector_t *selector, const xmlNode *node) +{ + if (node->doc != NULL && node->doc->dict != NULL) { + const xmlChar *interned = xmlDictExists(node->doc->dict, selector->name.data, selector->name.length); + if (interned != NULL) { + entry->id.name = interned; + } else { + entry->id.name = selector->name.data; + } + } else { + entry->id.name = selector->name.data; + } +} + +static zend_always_inline void lxb_selectors_adapted_set_entry_id(lxb_selectors_entry_t *entry, const lxb_css_selector_t *selector, const xmlNode *node) +{ + if (entry->id.name == NULL) { + lxb_selectors_adapted_set_entry_id_ex(entry, selector, node); + } +} + +static lxb_status_t +lxb_selectors_state_tree(lxb_selectors_t *selectors, const xmlNode *root, + const lxb_css_selector_list_t *list); + +static lxb_status_t +lxb_selectors_state_run(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_list_t *list); + +static lxb_selectors_entry_t * +lxb_selectors_state_find(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +static lxb_selectors_entry_t * +lxb_selectors_state_find_check(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_t *selector, + lxb_selectors_entry_t *entry); + +static lxb_selectors_entry_t * +lxb_selectors_state_pseudo_class_function(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +static const xmlNode * +lxb_selectors_next_node(lxb_selectors_nested_t *main); + +static const xmlNode * +lxb_selectors_state_has_relative(const xmlNode *node, + const lxb_css_selector_t *selector); + +static lxb_selectors_entry_t * +lxb_selectors_state_after_find_has(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +static lxb_selectors_entry_t * +lxb_selectors_state_after_find(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +static lxb_selectors_entry_t * +lxb_selectors_state_after_nth_child(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +static bool +lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *node); + +static bool +lxb_selectors_match_element(const lxb_css_selector_t *selector, + const xmlNode *node, lxb_selectors_entry_t *entry); + +static bool +lxb_selectors_match_id(const lxb_css_selector_t *selector, const xmlNode *node, bool quirks); + +static bool +lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src, + bool quirks); + +static bool +lxb_selectors_match_attribute(const lxb_css_selector_t *selector, + const xmlNode *node, lxb_selectors_entry_t *entry); + +static bool +lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, + const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_function(const lxb_css_selector_t *selector, + const xmlNode *node); + +static bool +lxb_selectors_pseudo_element(const lxb_css_selector_t *selector, + const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_disabled(const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_first_child(const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_first_of_type(const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_last_child(const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_last_of_type(const xmlNode *node); + +static bool +lxb_selectors_pseudo_class_read_write(const xmlNode *node); + +static bool +lxb_selectors_anb_calc(lxb_css_selector_anb_of_t *anb, size_t index); + +static lxb_status_t +lxb_selectors_cb_ok(const xmlNode *node, + lxb_css_selector_specificity_t spec, void *ctx); + +static lxb_status_t +lxb_selectors_cb_not(const xmlNode *node, + lxb_css_selector_specificity_t spec, void *ctx); + + +lxb_status_t +lxb_selectors_init(lxb_selectors_t *selectors) +{ + lxb_status_t status; + + selectors->objs = lexbor_dobject_create(); + status = lexbor_dobject_init(selectors->objs, + 128, sizeof(lxb_selectors_entry_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + selectors->nested = lexbor_dobject_create(); + status = lexbor_dobject_init(selectors->nested, + 64, sizeof(lxb_selectors_nested_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + selectors->options = LXB_SELECTORS_OPT_DEFAULT; + + return LXB_STATUS_OK; +} + +void +lxb_selectors_clean(lxb_selectors_t *selectors) +{ + lexbor_dobject_clean(selectors->objs); + lexbor_dobject_clean(selectors->nested); +} + +void +lxb_selectors_destroy(lxb_selectors_t *selectors) +{ + selectors->objs = lexbor_dobject_destroy(selectors->objs, true); + selectors->nested = lexbor_dobject_destroy(selectors->nested, true); +} + +lxb_inline const xmlNode * +lxb_selectors_descendant(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, + const xmlNode *node) +{ + node = node->parent; + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE) + && lxb_selectors_match(selectors, entry, selector, node)) + { + return node; + } + + node = node->parent; + } + + return NULL; +} + +lxb_inline const xmlNode * +lxb_selectors_close(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *node) +{ + if (lxb_selectors_match(selectors, entry, selector, node)) { + return node; + } + + return NULL; +} + +lxb_inline const xmlNode * +lxb_selectors_child(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *root) +{ + root = root->parent; + + if (root != NULL && CMP_NODE_TYPE(root, XML_ELEMENT_NODE) + && lxb_selectors_match(selectors, entry, selector, root)) + { + return root; + } + + return NULL; +} + +lxb_inline const xmlNode * +lxb_selectors_sibling(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *node) +{ + node = node->prev; + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + if (lxb_selectors_match(selectors, entry, selector, node)) { + return node; + } + + return NULL; + } + + node = node->prev; + } + + return NULL; +} + +lxb_inline const xmlNode * +lxb_selectors_following(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *node) +{ + node = node->prev; + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE) && + lxb_selectors_match(selectors, entry, selector, node)) + { + return node; + } + + node = node->prev; + } + + return NULL; +} + +lxb_status_t +lxb_selectors_find(lxb_selectors_t *selectors, const xmlNode *root, + const lxb_css_selector_list_t *list, + lxb_selectors_cb_f cb, void *ctx) +{ + lxb_selectors_entry_t *entry; + lxb_selectors_nested_t nested; + + entry = lexbor_dobject_calloc(selectors->objs); + + entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + entry->selector = list->last; + + nested.parent = NULL; + nested.entry = entry; + nested.cb = cb; + nested.ctx = ctx; + + selectors->current = &nested; + selectors->status = LXB_STATUS_OK; + + return lxb_selectors_state_tree(selectors, root, list); +} + +lxb_status_t +lxb_selectors_match_node(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_list_t *list, + lxb_selectors_cb_f cb, void *ctx) +{ + lxb_status_t status; + lxb_selectors_entry_t *entry; + lxb_selectors_nested_t nested; + + if (!CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + return LXB_STATUS_OK; + } + + entry = lexbor_dobject_calloc(selectors->objs); + + entry->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + entry->selector = list->last; + + nested.parent = NULL; + nested.entry = entry; + nested.cb = cb; + nested.ctx = ctx; + + selectors->current = &nested; + selectors->status = LXB_STATUS_OK; + + status = lxb_selectors_state_run(selectors, node, list); + + lxb_selectors_clean(selectors); + + return status; +} + +static lxb_status_t +lxb_selectors_state_tree(lxb_selectors_t *selectors, const xmlNode *root, + const lxb_css_selector_list_t *list) +{ + lxb_status_t status; + const xmlNode *node; + + if (selectors->options & LXB_SELECTORS_OPT_MATCH_ROOT) { + node = root; + + if (CMP_NODE_TYPE(node, XML_DOCUMENT_NODE) || CMP_NODE_TYPE(node, XML_HTML_DOCUMENT_NODE) + || CMP_NODE_TYPE(node, XML_DOCUMENT_FRAG_NODE)) { + node = root->children; + } + } + else { + node = root->children; + } + + if (node == NULL) { + goto out; + } + + do { + if (!CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + goto next; + } + + status = lxb_selectors_state_run(selectors, node, list); + if (status != LXB_STATUS_OK) { + if (status == LXB_STATUS_STOP) { + break; + } + + lxb_selectors_clean(selectors); + + return status; + } + + if (node->children != NULL) { + node = node->children; + } + else { + + next: + + while (node != root && node->next == NULL) { + node = node->parent; + } + + if (node == root) { + break; + } + + node = node->next; + } + } + while (true); + +out: + lxb_selectors_clean(selectors); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_selectors_state_run(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_list_t *list) +{ + lxb_selectors_entry_t *entry; + + entry = selectors->current->entry; + + entry->node = node; + selectors->state = lxb_selectors_state_find; + selectors->first = entry; + +again: + + do { + entry = selectors->state(selectors, entry); + } + while (entry != NULL); + + if (selectors->current->parent != NULL + && selectors->status == LXB_STATUS_OK) + { + entry = selectors->current->entry; + selectors->state = selectors->current->return_state; + + goto again; + } + + return selectors->status; +} + +static lxb_selectors_entry_t * +lxb_selectors_state_find(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry) +{ + const xmlNode *node; + lxb_selectors_entry_t *next; + const lxb_css_selector_t *selector; + const lxb_css_selector_anb_of_t *anb; + const lxb_css_selector_pseudo_t *pseudo; + + selector = entry->selector; + + if (selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION) { + pseudo = &selector->u.pseudo; + + /* Optimizing. */ + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + anb = pseudo->data; + + if (anb->of != NULL) { + break; + } + + goto godoit; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + goto godoit; + + default: + break; + } + + if (entry->nested == NULL) { + next = lexbor_dobject_calloc(selectors->objs); + + next->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + + entry->nested = lexbor_dobject_calloc(selectors->nested); + + entry->nested->entry = next; + entry->nested->parent = selectors->current; + } + + selectors->state = lxb_selectors_state_pseudo_class_function; + selectors->current->last = entry; + selectors->current = entry->nested; + + next = entry->nested->entry; + next->node = entry->node; + + return next; + } + +godoit: + + switch (entry->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + node = lxb_selectors_descendant(selectors, entry, + selector, entry->node); + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + node = lxb_selectors_close(selectors, entry, + selector, entry->node); + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + node = lxb_selectors_child(selectors, entry, + selector, entry->node); + break; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + node = lxb_selectors_sibling(selectors, entry, + selector, entry->node); + break; + + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = lxb_selectors_following(selectors, entry, + selector, entry->node); + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + default: + selectors->status = LXB_STATUS_ERROR; + return NULL; + } + + return lxb_selectors_state_find_check(selectors, node, selector, entry); +} + +static lxb_selectors_entry_t * +lxb_selectors_state_find_check(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_t *selector, + lxb_selectors_entry_t *entry) +{ + lxb_selectors_entry_t *next; + lxb_selectors_nested_t *current; + + if (node == NULL) { + + try_next: + + if (entry->next == NULL) { + + try_next_list: + + if (selector->list->next == NULL) { + return NULL; + } + + /* + * Try the following selectors from the selector list. + */ + + if (entry->following != NULL) { + entry->following->node = entry->node; + + if (selectors->current->parent == NULL) { + selectors->first = entry->following; + } + + return entry->following; + } + + next = lexbor_dobject_calloc(selectors->objs); + + next->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + next->selector = selector->list->next->last; + next->node = entry->node; + + entry->following = next; + + if (selectors->current->parent == NULL) { + selectors->first = next; + } + + return next; + } + + do { + entry = entry->next; + + while (entry->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + if (entry->next == NULL) { + selector = entry->selector; + goto try_next; + } + + entry = entry->next; + } + + switch (entry->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + node = entry->node->parent; + + if (node == NULL + || !CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) + { + node = NULL; + } + + break; + + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = entry->node->prev; + break; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + node = NULL; + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + default: + selectors->status = LXB_STATUS_ERROR; + return NULL; + } + } + while (node == NULL); + + entry->node = node; + + return entry; + } + + if (selector->prev == NULL) { + current = selectors->current; + + selectors->status = current->cb(current->entry->node, + selector->list->specificity, + current->ctx); + + if ((selectors->options & LXB_SELECTORS_OPT_MATCH_FIRST) == 0 + && current->parent == NULL) + { + if (selectors->status == LXB_STATUS_OK) { + entry = selectors->first; + goto try_next_list; + } + } + + return NULL; + } + + if (entry->prev == NULL) { + next = lexbor_dobject_calloc(selectors->objs); + + next->combinator = selector->combinator; + next->selector = selector->prev; + next->node = node; + + next->next = entry; + entry->prev = next; + + return next; + } + + entry->prev->node = node; + + return entry->prev; +} + +static lxb_selectors_entry_t * +lxb_selectors_state_pseudo_class_function(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry) +{ + const xmlNode *node, *base; + lxb_selectors_nested_t *current; + const lxb_css_selector_list_t *list; + lxb_css_selector_anb_of_t *anb; + const lxb_css_selector_pseudo_t *pseudo; + + current = selectors->current; + + base = lxb_selectors_next_node(current); + if (base == NULL) { + goto not_found; + } + + pseudo = ¤t->parent->last->selector->u.pseudo; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: + list = (lxb_css_selector_list_t *) pseudo->data; + node = lxb_selectors_state_has_relative(base, list->first); + + if (node == NULL) { + selectors->current = selectors->current->parent; + entry = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, NULL, + entry->selector, entry); + } + + current->root = base; + + current->entry->selector = list->last; + current->entry->node = node; + current->return_state = lxb_selectors_state_after_find_has; + current->cb = lxb_selectors_cb_ok; + current->ctx = ¤t->found; + current->found = false; + + selectors->state = lxb_selectors_state_find; + + return entry; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: + current->entry->selector = ((lxb_css_selector_list_t *) pseudo->data)->last; + current->entry->node = base; + current->return_state = lxb_selectors_state_after_find; + current->cb = lxb_selectors_cb_ok; + current->ctx = ¤t->found; + current->found = false; + + selectors->state = lxb_selectors_state_find; + + return entry; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: + current->entry->selector = ((lxb_css_selector_list_t *) pseudo->data)->last; + current->entry->node = base; + current->return_state = lxb_selectors_state_after_find; + current->cb = lxb_selectors_cb_not; + current->ctx = ¤t->found; + current->found = true; + + selectors->state = lxb_selectors_state_find; + + return entry; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + anb = pseudo->data; + + current->entry->selector = anb->of->last; + current->entry->node = base; + current->return_state = lxb_selectors_state_after_nth_child; + current->cb = lxb_selectors_cb_ok; + current->ctx = ¤t->found; + current->root = base; + current->index = 0; + current->found = false; + + selectors->state = lxb_selectors_state_find; + + return entry; + + /* + * This one can only happen if the user has somehow messed up the + * selector. + */ + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + default: + break; + } + +not_found: + + selectors->current = selectors->current->parent; + entry = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, NULL, + entry->selector, entry); +} + +static const xmlNode * +lxb_selectors_next_node(lxb_selectors_nested_t *main) +{ + const xmlNode *node = main->entry->node; + + switch (main->parent->last->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + if (node->parent == NULL + || !CMP_NODE_TYPE(node->parent, XML_ELEMENT_NODE)) + { + return NULL; + } + + return node->parent; + + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + return node; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = node->prev; + break; + + default: + return NULL; + } + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + break; + } + + node = node->prev; + } + + return node; +} + +static const xmlNode * +lxb_selectors_state_has_relative(const xmlNode *node, + const lxb_css_selector_t *selector) +{ + const xmlNode *root = node; + + switch (selector->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + node = node->children; + break; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = node->next; + break; + + default: + return NULL; + } + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + break; + } + + while (node !=root && node->next == NULL) { + node = node->parent; + } + + if (node == root) { + return NULL; + } + + node = node->next; + } + + return node; +} + +static lxb_selectors_entry_t * +lxb_selectors_state_after_find_has(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry) +{ + const xmlNode *node; + lxb_selectors_entry_t *parent; + lxb_selectors_nested_t *current; + + if (selectors->current->found) { + node = selectors->current->root; + + selectors->current = selectors->current->parent; + parent = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, node, + parent->selector, parent); + } + + current = selectors->current; + node = entry->node; + + switch (entry->selector->list->first->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + if (node->children != NULL) { + node = node->children; + } + else { + + next: + + while (node != current->root && node->next == NULL) { + node = node->parent; + } + + if (node == current->root) { + goto failed; + } + + node = node->next; + } + + if (!CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + goto next; + } + + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = node->next; + + while (node != NULL && !CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + node = node->next; + } + + if (node == NULL) { + goto failed; + } + + break; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + goto failed; + + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + default: + selectors->status = LXB_STATUS_ERROR; + return NULL; + } + + entry->node = node; + selectors->state = lxb_selectors_state_find; + + return entry; + +failed: + + selectors->current = selectors->current->parent; + parent = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, NULL, + parent->selector, parent); +} + + +static lxb_selectors_entry_t * +lxb_selectors_state_after_find(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry) +{ + const xmlNode *node; + lxb_selectors_entry_t *parent; + lxb_selectors_nested_t *current; + + current = selectors->current; + + if (current->found) { + node = entry->node; + + selectors->current = current->parent; + parent = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, node, + parent->selector, parent); + } + + node = entry->node; + + switch (current->parent->last->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + if (node->parent != NULL + && CMP_NODE_TYPE(node->parent, XML_ELEMENT_NODE)) + { + node = node->parent; + } + else { + node = NULL; + } + + break; + + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + node = node->prev; + + while (node != NULL && !CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + node = node->prev; + } + + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + node = NULL; + break; + + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + default: + selectors->status = LXB_STATUS_ERROR; + return NULL; + } + + if (node == NULL) { + selectors->current = current->parent; + parent = selectors->current->last; + + selectors->state = lxb_selectors_state_find; + + return lxb_selectors_state_find_check(selectors, node, + parent->selector, parent); + } + + entry->node = node; + selectors->state = lxb_selectors_state_find; + + return entry; +} + +static lxb_selectors_entry_t * +lxb_selectors_state_after_nth_child(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry) +{ + bool found; + const xmlNode *node; + lxb_selectors_entry_t *parent; + lxb_selectors_nested_t *current; + const lxb_css_selector_t *selector; + const lxb_css_selector_pseudo_t *pseudo; + + current = selectors->current; + selector = current->parent->last->selector; + pseudo = &selector->u.pseudo; + + node = entry->node; + + if (current->found) { + current->index += 1; + } + else if (current->root == node) { + node = NULL; + goto done; + } + + if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD) { + node = node->prev; + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + break; + } + + node = node->prev; + } + } + else { + node = node->next; + + while (node != NULL) { + if (CMP_NODE_TYPE(node, XML_ELEMENT_NODE)) { + break; + } + + node = node->next; + } + } + + if (node == NULL) { + goto done; + } + + entry->node = node; + current->found = false; + selectors->state = lxb_selectors_state_find; + + return entry; + +done: + + if (current->index > 0) { + found = lxb_selectors_anb_calc(pseudo->data, current->index); + + node = (found) ? current->root : NULL; + } + + selectors->state = lxb_selectors_state_find; + selectors->current = selectors->current->parent; + + parent = selectors->current->last; + + return lxb_selectors_state_find_check(selectors, node, + parent->selector, parent); +} + +static bool +lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, + const lxb_css_selector_t *selector, const xmlNode *node) +{ + switch (selector->type) { + case LXB_CSS_SELECTOR_TYPE_ANY: + return true; + + case LXB_CSS_SELECTOR_TYPE_ELEMENT: + return lxb_selectors_match_element(selector, node, entry); + + case LXB_CSS_SELECTOR_TYPE_ID: + return lxb_selectors_match_id(selector, node, selectors->options & LXB_SELECTORS_OPT_QUIRKS_MODE); + + case LXB_CSS_SELECTOR_TYPE_CLASS: { + const xmlAttr *dom_attr = lxb_selectors_adapted_attr(node, (const lxb_char_t *) "class"); + if (dom_attr == NULL) { + return false; + } + + lexbor_str_t trg = lxb_selectors_adapted_attr_value_empty(dom_attr); + + if (trg.length == 0) { + return false; + } + + return lxb_selectors_match_class(&trg, + &selector->name, selectors->options & LXB_SELECTORS_OPT_QUIRKS_MODE); + } + + case LXB_CSS_SELECTOR_TYPE_ATTRIBUTE: + return lxb_selectors_match_attribute(selector, node, entry); + + case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS: + return lxb_selectors_pseudo_class(selector, node); + + case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION: + return lxb_selectors_pseudo_class_function(selector, node); + + case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT: + return lxb_selectors_pseudo_element(selector, node); + + case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION: + return false; + + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return false; +} + +static bool +lxb_selectors_match_element(const lxb_css_selector_t *selector, + const xmlNode *node, lxb_selectors_entry_t *entry) +{ + lxb_selectors_adapted_set_entry_id(entry, selector, node); + return lxb_selectors_adapted_cmp_local_name_id(node, &entry->id); +} + +static bool +lxb_selectors_match_id(const lxb_css_selector_t *selector, const xmlNode *node, bool quirks) +{ + const xmlAttr *dom_attr = lxb_selectors_adapted_attr(node, (const lxb_char_t *) "id"); + if (dom_attr == NULL) { + return false; + } + + const lexbor_str_t *src = &selector->name; + lexbor_str_t trg = lxb_selectors_adapted_attr_value_empty(dom_attr); + if (trg.length == src->length) { + if (quirks) { + return lexbor_str_data_ncasecmp(trg.data, src->data, src->length); + } else { + return lexbor_str_data_ncmp(trg.data, src->data, src->length); + } + } + + return false; +} + +static bool +lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src, + bool quirks) +{ + lxb_char_t chr; + + if (target->length < src->length) { + return false; + } + + bool is_it = false; + + const lxb_char_t *data = target->data; + const lxb_char_t *pos = data; + const lxb_char_t *end = data + target->length; + + for (; data < end; data++) { + chr = *data; + + if (lexbor_utils_whitespace(chr, ==, ||)) { + + if ((size_t) (data - pos) == src->length) { + if (quirks) { + is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length); + } + else { + is_it = lexbor_str_data_ncmp(pos, src->data, src->length); + } + + if (is_it) { + return true; + } + } + + if ((size_t) (end - data) < src->length) { + return false; + } + + pos = data + 1; + } + } + + if ((size_t) (end - pos) == src->length && src->length != 0) { + if (quirks) { + is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length); + } + else { + is_it = lexbor_str_data_ncmp(pos, src->data, src->length); + } + } + + return is_it; +} + +static bool +lxb_selectors_match_attribute(const lxb_css_selector_t *selector, + const xmlNode *node, lxb_selectors_entry_t *entry) +{ + bool res, ins; + const xmlAttr *dom_attr; + lexbor_str_t trg; + const lexbor_str_t *src; + const lxb_css_selector_attribute_t *attr; + + attr = &selector->u.attribute; + + lxb_selectors_adapted_set_entry_id(entry, selector, node); + + dom_attr = lxb_selectors_adapted_attr(node, entry->id.name); + if (dom_attr == NULL) { + return false; + } + + src = &attr->value; + + if (src->data == NULL) { + return true; + } + + trg = lxb_selectors_adapted_attr_value_empty(dom_attr); + + ins = attr->modifier == LXB_CSS_SELECTOR_MODIFIER_I; + + switch (attr->match) { + case LXB_CSS_SELECTOR_MATCH_EQUAL: /* = */ + if (trg.length == src->length) { + if (ins) { + return lexbor_str_data_ncasecmp(trg.data, src->data, + src->length); + } + + return lexbor_str_data_ncmp(trg.data, src->data, + src->length); + } + + return false; + + case LXB_CSS_SELECTOR_MATCH_INCLUDE: /* ~= */ + return lxb_selectors_match_class(&trg, src, ins); + + case LXB_CSS_SELECTOR_MATCH_DASH: /* |= */ + if (trg.length == src->length) { + if (ins) { + return lexbor_str_data_ncasecmp(trg.data, src->data, + src->length); + } + + return lexbor_str_data_ncmp(trg.data, src->data, + src->length); + } + + if (trg.length > src->length) { + if (ins) { + res = lexbor_str_data_ncasecmp(trg.data, + src->data, src->length); + } + else { + res = lexbor_str_data_ncmp(trg.data, + src->data, src->length); + } + + if (res && trg.data[src->length] == '-') { + return true; + } + } + + return false; + + case LXB_CSS_SELECTOR_MATCH_PREFIX: /* ^= */ + if (src->length != 0 && trg.length >= src->length) { + if (ins) { + return lexbor_str_data_ncasecmp(trg.data, src->data, + src->length); + } + + return lexbor_str_data_ncmp(trg.data, src->data, + src->length); + } + + return false; + + case LXB_CSS_SELECTOR_MATCH_SUFFIX: /* $= */ + if (src->length != 0 && trg.length >= src->length) { + size_t dif = trg.length - src->length; + + if (ins) { + return lexbor_str_data_ncasecmp(trg.data + dif, + src->data, src->length); + } + + return lexbor_str_data_ncmp(trg.data + dif, src->data, + src->length); + } + + return false; + + case LXB_CSS_SELECTOR_MATCH_SUBSTRING: /* *= */ + if (src->length == 0) { + return false; + } + + if (ins) { + return lexbor_str_data_ncasecmp_contain(trg.data, trg.length, + src->data, src->length); + } + + return lexbor_str_data_ncmp_contain(trg.data, trg.length, + src->data, src->length); + EMPTY_SWITCH_DEFAULT_CASE(); + } + + return false; +} + +static bool +lxb_selectors_pseudo_class(const lxb_css_selector_t *selector, + const xmlNode *node) +{ + const lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo; + + static const lxb_char_t checkbox[] = "checkbox"; + static const size_t checkbox_length = sizeof(checkbox) / sizeof(lxb_char_t) - 1; + + static const lxb_char_t radio[] = "radio"; + static const size_t radio_length = sizeof(radio) / sizeof(lxb_char_t) - 1; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE: + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK: + /* https://drafts.csswg.org/selectors/#the-any-link-pseudo */ + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "a") + || lxb_selectors_adapted_cmp_local_name_literal(node, "area"))) + { + return lxb_selectors_adapted_has_attr(node, "href"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK: + if (!EG(exception)) { + php_dom_throw_error_with_message(NOT_SUPPORTED_ERR, ":blank selector is not implemented because CSSWG has not yet decided its semantics (https://github.com/w3c/csswg-drafts/issues/1967)", true); + } + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED: + /* https://drafts.csswg.org/selectors/#checked */ + if (!php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) { + return false; + } + if (lxb_selectors_adapted_cmp_local_name_literal(node, "input")) { + const xmlAttr *dom_attr = lxb_selectors_adapted_attr(node, (const lxb_char_t *) "type"); + if (dom_attr == NULL) { + return false; + } + + lexbor_str_t str = lxb_selectors_adapted_attr_value_nullable(dom_attr); + + if (str.data == NULL) { + return false; + } + + if(str.length == 8) { + if (lexbor_str_data_ncasecmp(checkbox, str.data, checkbox_length)) { + return lxb_selectors_adapted_has_attr(node, "checked"); + } + } + else if(str.length == 5) { + if (lexbor_str_data_ncasecmp(radio, str.data, radio_length)) { + return lxb_selectors_adapted_has_attr(node, "checked"); + } + } + } + else if(lxb_selectors_adapted_cmp_local_name_literal(node, "option")) { + return lxb_selectors_adapted_has_attr(node, "selected"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT: + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED: + return lxb_selectors_pseudo_class_disabled(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY: + node = node->children; + + while (node != NULL) { + /* Following https://developer.mozilla.org/en-US/docs/Web/CSS/:empty, i.e. what currently happens in browsers, + * not the CSS Selectors Level 4 Draft that no one implements yet. */ + if (!CMP_NODE_TYPE(node, XML_COMMENT_NODE) && !CMP_NODE_TYPE(node, XML_PI_NODE)) { + return false; + } + + node = node->next; + } + + return true; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED: + return !lxb_selectors_pseudo_class_disabled(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD: + return lxb_selectors_pseudo_class_first_child(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE: + return lxb_selectors_pseudo_class_first_of_type(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD: + return lxb_selectors_pseudo_class_last_child(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE: + return lxb_selectors_pseudo_class_last_of_type(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK: + /* https://html.spec.whatwg.org/multipage/semantics-other.html#selector-link */ + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "a") + || lxb_selectors_adapted_cmp_local_name_literal(node, "area"))) + { + return lxb_selectors_adapted_has_attr(node, "href"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD: + return lxb_selectors_pseudo_class_first_child(node) + && lxb_selectors_pseudo_class_last_child(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE: + return lxb_selectors_pseudo_class_first_of_type(node) + && lxb_selectors_pseudo_class_last_of_type(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL: + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") + || lxb_selectors_adapted_cmp_local_name_literal(node, "select") + || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + { + return !lxb_selectors_adapted_has_attr(node, "required"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN: + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") + || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + { + return lxb_selectors_adapted_has_attr(node, "placeholder"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY: + return !lxb_selectors_pseudo_class_read_write(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE: + return lxb_selectors_pseudo_class_read_write(node); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED: + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") + || lxb_selectors_adapted_cmp_local_name_literal(node, "select") + || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + { + return lxb_selectors_adapted_has_attr(node, "required"); + } + + return false; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT: + return node->parent != NULL + && (node->parent->type == XML_DOCUMENT_FRAG_NODE || node->parent->type == XML_DOCUMENT_NODE + || node->parent->type == XML_HTML_DOCUMENT_NODE); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING: + break; + } + + return false; +} + +static bool +lxb_selectors_pseudo_class_function(const lxb_css_selector_t *selector, + const xmlNode *node) +{ + size_t index; + const xmlNode *base; + const lxb_css_selector_pseudo_t *pseudo; + + pseudo = &selector->u.pseudo; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + index = 0; + + if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD) { + while (node != NULL) { + if (lxb_selectors_adapted_is_matchable_child(node)) + { + index++; + } + + node = node->prev; + } + } + else { + while (node != NULL) { + if (lxb_selectors_adapted_is_matchable_child(node)) + { + index++; + } + + node = node->next; + } + } + + return lxb_selectors_anb_calc(pseudo->data, index); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + index = 0; + base = node; + + if (pseudo->type == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE) { + while (node != NULL) { + if(xmlStrEqual(node->name, base->name) + && lxb_selectors_adapted_cmp_ns(node, base)) + { + index++; + } + + node = node->prev; + } + } + else { + while (node != NULL) { + if(xmlStrEqual(node->name, base->name) + && lxb_selectors_adapted_cmp_ns(node, base)) + { + index++; + } + + node = node->next; + } + } + + return lxb_selectors_anb_calc(pseudo->data, index); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + default: + break; + } + + return false; +} + +static bool +lxb_selectors_pseudo_element(const lxb_css_selector_t *selector, + const xmlNode *node) +{ + const lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT: + break; + } + + return false; +} + +/* https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled */ +static bool +lxb_selectors_pseudo_class_disabled(const xmlNode *node) +{ + if (!php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token)) { + return false; + } + + if (lxb_selectors_adapted_has_attr(node, "disabled") + && (lxb_selectors_adapted_cmp_local_name_literal(node, "button") + || lxb_selectors_adapted_cmp_local_name_literal(node, "input") + || lxb_selectors_adapted_cmp_local_name_literal(node, "select") + || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea") + || lxb_selectors_adapted_cmp_local_name_literal(node, "optgroup") + || lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset"))) + { + return true; + } + + if (lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset")) { + const xmlNode *fieldset = node; + node = node->parent; + + while (node != NULL && lxb_selectors_adapted_is_matchable_child(node)) { + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && lxb_selectors_adapted_cmp_local_name_literal(node, "fieldset") + && lxb_selectors_adapted_has_attr(node, "disabled")) + { + /* Search first legend child and figure out if fieldset is a descendent from that. */ + const xmlNode *search_current = node->children; + do { + if (search_current->type == XML_ELEMENT_NODE + && php_dom_ns_is_fast(search_current, php_dom_ns_is_html_magic_token) + && lxb_selectors_adapted_cmp_local_name_literal(search_current, "legend")) { + /* search_current is a legend element. */ + const xmlNode *inner_search_current = fieldset; + + /* Disabled does not apply if fieldset is a descendant from search_current */ + do { + if (inner_search_current == search_current) { + return false; + } + + inner_search_current = inner_search_current->parent; + } while (inner_search_current != NULL); + + return true; + } + + search_current = search_current->next; + } while (search_current != NULL); + } + + node = node->parent; + } + } + + return false; +} + +static bool +lxb_selectors_pseudo_class_first_child(const xmlNode *node) +{ + node = node->prev; + + while (node != NULL) { + if (lxb_selectors_adapted_is_matchable_child(node)) + { + return false; + } + + node = node->prev; + } + + return true; +} + +static bool +lxb_selectors_pseudo_class_first_of_type(const xmlNode *node) +{ + const xmlNode *root = node; + node = node->prev; + + while (node) { + if (xmlStrEqual(node->name, root->name) + && lxb_selectors_adapted_cmp_ns(node, root)) + { + return false; + } + + node = node->prev; + } + + return true; +} + +static bool +lxb_selectors_pseudo_class_last_child(const xmlNode *node) +{ + node = node->next; + + while (node != NULL) { + if (lxb_selectors_adapted_is_matchable_child(node)) + { + return false; + } + + node = node->next; + } + + return true; +} + +static bool +lxb_selectors_pseudo_class_last_of_type(const xmlNode *node) +{ + const xmlNode *root = node; + node = node->next; + + while (node) { + if (xmlStrEqual(node->name, root->name) + && lxb_selectors_adapted_cmp_ns(node, root)) + { + return false; + } + + node = node->next; + } + + return true; +} + +static bool +lxb_selectors_pseudo_class_read_write(const xmlNode *node) +{ + if (php_dom_ns_is_fast(node, php_dom_ns_is_html_magic_token) + && (lxb_selectors_adapted_cmp_local_name_literal(node, "input") + || lxb_selectors_adapted_cmp_local_name_literal(node, "textarea"))) + { + if (lxb_selectors_adapted_has_attr(node, "readonly")) { + return false; + } + + return !lxb_selectors_adapted_has_attr(node, "disabled"); + } + + return false; +} + +static bool +lxb_selectors_anb_calc(lxb_css_selector_anb_of_t *anb, size_t index) +{ + double num; + + if (anb->anb.a == 0) { + if (anb->anb.b >= 0 && (size_t) anb->anb.b == index) { + return true; + } + } + else { + num = ((double) index - (double) anb->anb.b) / (double) anb->anb.a; + + if (num >= 0.0f && (num - trunc(num)) == 0.0f) { + return true; + } + } + + return false; +} + +static lxb_status_t +lxb_selectors_cb_ok(const xmlNode *node, + lxb_css_selector_specificity_t spec, void *ctx) +{ + *((bool *) ctx) = true; + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_selectors_cb_not(const xmlNode *node, + lxb_css_selector_specificity_t spec, void *ctx) +{ + *((bool *) ctx) = false; + return LXB_STATUS_OK; +} diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h new file mode 100644 index 0000000000000..483226f4ce163 --- /dev/null +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021-2024 Alexander Borisov + * + * Author: Alexander Borisov + * Adapted for PHP libxml2 by: Niels Dossche + */ + + +#ifndef LEXBOR_SELECTORS_H +#define LEXBOR_SELECTORS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/selectors/base.h" +#include "lexbor/dom/dom.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/core/array_obj.h" +#include + + +typedef enum { + LXB_SELECTORS_OPT_DEFAULT = 0x00, + + /* + * Includes the passed (root) node in the search. + * + * By default, the root node does not participate in selector searches, + * only its children. + * + * This behavior is logical, if you have found a node and then you want to + * search for other nodes in it, you don't need to check it again. + * + * But there are cases when it is necessary for root node to participate + * in the search. That's what this option is for. + */ + LXB_SELECTORS_OPT_MATCH_ROOT = 1 << 1, + + /* + * Stop searching after the first match with any of the selectors + * in the list. + * + * By default, the callback will be triggered for each selector list. + * That is, if your node matches different selector lists, it will be + * returned multiple times in the callback. + * + * For example: + * HTML:
test
+ * Selectors: div, div[id="ok"], div:has(:not(a)) + * + * The default behavior will cause three callbacks with the same node (div). + * Because it will be found by every selector in the list. + * + * This option allows you to end the element check after the first match on + * any of the selectors. That is, the callback will be called only once + * for example above. This way we get rid of duplicates in the search. + */ + LXB_SELECTORS_OPT_MATCH_FIRST = 1 << 2, + + /* Quirks mode (sigh) */ + LXB_SELECTORS_OPT_QUIRKS_MODE = 1 << 3, +} +lxb_selectors_opt_t; + +typedef struct lxb_selectors lxb_selectors_t; +typedef struct lxb_selectors_entry lxb_selectors_entry_t; +typedef struct lxb_selectors_nested lxb_selectors_nested_t; + +typedef lxb_status_t +(*lxb_selectors_cb_f)(const xmlNode *node, + lxb_css_selector_specificity_t spec, void *ctx); + +typedef lxb_selectors_entry_t * +(*lxb_selectors_state_cb_f)(lxb_selectors_t *selectors, + lxb_selectors_entry_t *entry); + +typedef struct { + const xmlChar *name; +} lxb_selectors_adapted_id; + +struct lxb_selectors_entry { + lxb_selectors_adapted_id id; + lxb_css_selector_combinator_t combinator; + const lxb_css_selector_t *selector; + const xmlNode *node; + lxb_selectors_entry_t *next; + lxb_selectors_entry_t *prev; + lxb_selectors_entry_t *following; + lxb_selectors_nested_t *nested; +}; + +struct lxb_selectors_nested { + lxb_selectors_entry_t *entry; + lxb_selectors_state_cb_f return_state; + + lxb_selectors_cb_f cb; + void *ctx; + + const xmlNode *root; + lxb_selectors_entry_t *last; + lxb_selectors_nested_t *parent; + + size_t index; + bool found; +}; + +struct lxb_selectors { + lxb_selectors_state_cb_f state; + lexbor_dobject_t *objs; + lexbor_dobject_t *nested; + + lxb_selectors_nested_t *current; + lxb_selectors_entry_t *first; + + lxb_selectors_opt_t options; + lxb_status_t status; +}; + + +/* + * Initialization of lxb_selectors_t object. + * + * Caches are initialized in this function. + * + * @param[in] lxb_selectors_t * + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_selectors_init(lxb_selectors_t *selectors); + +/* + * Clears the object. Returns object to states as after initialization. + * + * After each call to lxb_selectors_find() and lxb_selectors_find_for_node(), + * the lxb_selectors_t object is cleared. That is, you don't need to call this + * function every time after searching by a selector. + * + * @param[in] lxb_url_parser_t * + */ +LXB_API void +lxb_selectors_clean(lxb_selectors_t *selectors); + +/* + * Destroy lxb_selectors_t object. + * + * Destroying all caches. + * + * @param[in] lxb_selectors_t *. Can be NULL. + * if true: destroys the lxb_selectors_t object and all internal caches. + */ +LXB_API void +lxb_selectors_destroy(lxb_selectors_t *selectors); + +/* + * Search for nodes by selector list. + * + * Default Behavior: + * 1. The root node does not participate in the search, only its child nodes. + * 2. If a node matches multiple selector lists, a callback with that node + * will be called on each list. + * For example: + * HTML:
+ * Selectors: div, div[id="ok"], div:has(:not(a)) + * For each selector list, a callback with a "div" node will be called. + * + * To change the search behavior, see lxb_selectors_opt_set(). + * + * @param[in] lxb_selectors_t *. + * @param[in] const xmlNode *. The node from which the search will begin. + * @param[in] const lxb_css_selector_list_t *. Selectors List. + * @param[in] lxb_selectors_cb_f. Callback for a found node. + * @param[in] void *. Context for the callback. + * if true: destroys the lxb_selectors_t object and all internal caches. + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_selectors_find(lxb_selectors_t *selectors, const xmlNode *root, + const lxb_css_selector_list_t *list, + lxb_selectors_cb_f cb, void *ctx); + +/* + * Match a node to a Selectors List. + * + * In other words, the function checks which selector lists will find the + * specified node. + * + * Default Behavior: + * 1. If a node matches multiple selector lists, a callback with that node + * will be called on each list. + * For example: + * HTML:
+ * Node: div + * Selectors: div, div[id="ok"], div:has(:not(a)) + * For each selector list, a callback with a "div" node will be called. + * + * To change the search behavior, see lxb_selectors_opt_set(). + * + * @param[in] lxb_selectors_t *. + * @param[in] const xmlNode *. The node from which the search will begin. + * @param[in] const lxb_css_selector_list_t *. Selectors List. + * @param[in] lxb_selectors_cb_f. Callback for a found node. + * @param[in] void *. Context for the callback. + * if true: destroys the lxb_selectors_t object and all internal caches. + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_selectors_match_node(lxb_selectors_t *selectors, const xmlNode *node, + const lxb_css_selector_list_t *list, + lxb_selectors_cb_f cb, void *ctx); + +/* + * Inline functions. + */ + +/* + * The function sets the node search options. + * + * For more information, see lxb_selectors_opt_t. + * + * @param[in] lxb_selectors_t *. + * @param[in] lxb_selectors_opt_t. + */ +lxb_inline void +lxb_selectors_opt_set(lxb_selectors_t *selectors, lxb_selectors_opt_t opt) +{ + selectors->options = opt; +} + +/* + * Get the current selector. + * + * Function to get the selector by which the node was found. + * Use context (void *ctx) to pass the lxb_selectors_t object to the callback. + * + * @param[in] const lxb_selectors_t *. + * + * @return const lxb_css_selector_list_t *. + */ +lxb_inline const lxb_css_selector_list_t * +lxb_selectors_selector(const lxb_selectors_t *selectors) +{ + return selectors->current->entry->selector->list; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_SELECTORS_H */ diff --git a/ext/dom/lexbor/lexbor/selectors/base.h b/ext/dom/lexbor/lexbor/selectors/base.h index c3994d9adf2ed..64af0261a75c3 100644 --- a/ext/dom/lexbor/lexbor/selectors/base.h +++ b/ext/dom/lexbor/lexbor/selectors/base.h @@ -15,7 +15,7 @@ extern "C" { #define LXB_SELECTORS_VERSION_MAJOR 0 -#define LXB_SELECTORS_VERSION_MINOR 2 +#define LXB_SELECTORS_VERSION_MINOR 3 #define LXB_SELECTORS_VERSION_PATCH 0 #define LXB_SELECTORS_VERSION_STRING LEXBOR_STRINGIZE(LXB_SELECTORS_VERSION_MAJOR) "." \ diff --git a/ext/dom/lexbor/lexbor/selectors/selectors.c b/ext/dom/lexbor/lexbor/selectors/selectors.c deleted file mode 100644 index ec2d8f4083cef..0000000000000 --- a/ext/dom/lexbor/lexbor/selectors/selectors.c +++ /dev/null @@ -1,1767 +0,0 @@ -/* - * Copyright (C) 2021 Alexander Borisov - * - * Author: Alexander Borisov - */ - -#include "lexbor/selectors/selectors.h" - -#include - - -static lxb_selectors_entry_t * -lxb_selectors_find_by_selector(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx); - -static lxb_selectors_entry_child_t * -lxb_selectors_next(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_child_t *child, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx); - -static lxb_selectors_entry_child_t * -lxb_selectors_current(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_child_t *child, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx); - -static lxb_selectors_entry_t * -lxb_selectors_next_by_selector(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx); - -static lxb_status_t -lxb_selectors_find_by(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_dom_node_t *root, lxb_dom_node_t *node, - lxb_css_selector_t *selector, lxb_selectors_cb_f cb, void *ctx); - -static lxb_status_t -lxb_selectors_find_by_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *node, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx); - -static bool -lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *node); - -static bool -lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src, - bool quirks); - -static bool -lxb_selectors_pseudo_class(lxb_css_selector_t *selector, lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_function(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, - lxb_dom_node_t *node, - lxb_selectors_entry_t *entry); - -static bool -lxb_selectors_pseudo_element(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, - lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_element_function(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, - lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_disabled(lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_first_child(lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_first_of_type(lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_last_child(lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_last_of_type(lxb_dom_node_t *node); - -static bool -lxb_selectors_pseudo_class_read_write(lxb_dom_node_t *node); - -static lxb_status_t -lxb_selectors_first_match(lxb_dom_node_t *node, - lxb_css_selector_specificity_t spec, void *ctx); - - -lxb_selectors_t * -lxb_selectors_create(void) -{ - return lexbor_calloc(1, sizeof(lxb_selectors_t)); -} - -lxb_status_t -lxb_selectors_init(lxb_selectors_t *selectors) -{ - lxb_status_t status; - - if (selectors == NULL) { - return LXB_STATUS_ERROR_INCOMPLETE_OBJECT; - } - - selectors->objs = lexbor_dobject_create(); - status = lexbor_dobject_init(selectors->objs, - 128, sizeof(lxb_selectors_entry_t)); - if (status != LXB_STATUS_OK) { - return status; - } - - selectors->chld = lexbor_dobject_create(); - status = lexbor_dobject_init(selectors->chld, - 32, sizeof(lxb_selectors_entry_child_t)); - if (status != LXB_STATUS_OK) { - return status; - } - - return LXB_STATUS_OK; -} - -void -lxb_selectors_clean(lxb_selectors_t *selectors) -{ - lexbor_dobject_clean(selectors->objs); - lexbor_dobject_clean(selectors->chld); -} - -lxb_selectors_t * -lxb_selectors_destroy(lxb_selectors_t *selectors, bool self_destroy) -{ - if (selectors == NULL) { - return NULL; - } - - selectors->objs = lexbor_dobject_destroy(selectors->objs, true); - selectors->chld = lexbor_dobject_destroy(selectors->chld, true); - - if (self_destroy) { - return lexbor_free(selectors); - } - - return selectors; -} - - -lxb_inline lxb_dom_node_t * -lxb_selectors_descendant(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *root, - lxb_dom_node_t *node) -{ - do { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - if (node->first_child != NULL) { - node = node->first_child; - } - else { - while (node != root && node->next == NULL) { - node = node->parent; - } - - if (node == root) { - return NULL; - } - - node = node->next; - } - } - while (true); -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_descendant_next(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *root, - lxb_dom_node_t *node) -{ - do { - if (node->first_child != NULL) { - node = node->first_child; - } - else { - while (node != root && node->next == NULL) { - node = node->parent; - } - - if (node == root) { - return NULL; - } - - node = node->next; - } - - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - } - while (true); -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_descendant_reverse(lxb_selectors_t *selectors, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_dom_node_t *node) -{ - node = node->parent; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - node = node->parent; - } - - return false; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_close(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - if (lxb_selectors_match(selectors, entry, selector, node)) { - return node; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_child(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *root, - lxb_dom_node_t *node) -{ - node = root->first_child; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - node = node->next; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_child_next(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *root, - lxb_dom_node_t *node) -{ - node = node->next; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - node = node->next; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_sibling(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - node = node->next; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) { - if (lxb_selectors_match(selectors, entry, selector, node)) { - return node; - } - - return NULL; - } - - node = node->next; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_sibling_reverse(lxb_selectors_t *selectors, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_dom_node_t *node) -{ - node = node->prev; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT) { - if (lxb_selectors_match(selectors, entry, selector, node)) { - return node; - } - - return NULL; - } - - node = node->prev; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_following(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - node = node->next; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - node = node->next; - } - - return NULL; -} - -lxb_inline lxb_dom_node_t * -lxb_selectors_following_reverse(lxb_selectors_t *selectors, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_dom_node_t *node) -{ - node = node->prev; - - while (node != NULL) { - if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && - lxb_selectors_match(selectors, entry, selector, node)) - { - return node; - } - - node = node->prev; - } - - return NULL; -} - -lxb_inline bool -lxb_selectors_anb_calc(lxb_css_selector_anb_of_t *anb, size_t index) -{ - double num; - - if (anb->anb.a == 0) { - if (anb->anb.b >= 0 && (size_t) anb->anb.b == index) { - return true; - } - } - else { - num = ((double) index - (double) anb->anb.b) / (double) anb->anb.a; - - if (num >= 0.0f && (num - trunc(num)) == 0.0f) { - return true; - } - } - - return false; -} - -lxb_status_t -lxb_selectors_find(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx) -{ - lxb_selectors_entry_t *child; - - while (list != NULL) { - child = lxb_selectors_next_by_selector(selectors, root, NULL, - list->first, cb, ctx); - if (child == NULL) { - return LXB_STATUS_ERROR; - } - - list = list->next; - } - - lxb_selectors_clean(selectors); - - return LXB_STATUS_OK; -} - -lxb_status_t -lxb_selectors_find_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_status_t status; - - while (list != NULL) { - status = lxb_selectors_find_by_reverse(selectors, root, - list->last, cb, ctx); - if (status != LXB_STATUS_OK) { - return status; - } - - list = list->next; - } - - lxb_selectors_clean(selectors); - - return LXB_STATUS_OK; -} - -static lxb_selectors_entry_t * -lxb_selectors_find_by_selector(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_status_t status; - lxb_dom_node_t *node = root, *base = root; - - if (entry == NULL) { - entry = lexbor_dobject_calloc(selectors->objs); - if (entry == NULL) { - return NULL; - } - - entry->selector = selector; - } - - switch (selector->combinator) { - case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: - node = lxb_selectors_descendant(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: - node = lxb_selectors_close(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CHILD: - node = lxb_selectors_child(selectors, entry, selector, base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: - node = lxb_selectors_sibling(selectors, entry, selector, base); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: - node = lxb_selectors_following(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CELL: - default: - return NULL; - } - - status = lxb_selectors_find_by(selectors, entry, root, node, selector, - cb, ctx); - if (status != LXB_STATUS_OK) { - return NULL; - } - - return entry; -} - -static lxb_selectors_entry_child_t * -lxb_selectors_next(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_child_t *child, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_selectors_entry_child_t *chld_root = child; - - if (list == NULL) { - return NULL; - } - - if (child == NULL) { - child = lexbor_dobject_calloc(selectors->chld); - if (child == NULL) { - goto failed; - } - - chld_root = child; - } - - do { - child->entry = lxb_selectors_next_by_selector(selectors, root, - child->entry, list->first, - cb, ctx); - if (child->entry == NULL) { - return NULL; - } - - if (list->next == NULL) { - return chld_root; - } - - if (child->next == NULL) { - child->next = lexbor_dobject_calloc(selectors->chld); - if (child->next == NULL) { - goto failed; - } - } - - child = child->next; - list = list->next; - } - while (true); - - return chld_root; - -failed: - - selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; - - return NULL; -} - -static lxb_selectors_entry_child_t * -lxb_selectors_current(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_child_t *child, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_selectors_entry_child_t *chld_root = child; - - if (list == NULL) { - return NULL; - } - - if (child == NULL) { - child = lexbor_dobject_calloc(selectors->chld); - if (child == NULL) { - goto failed; - } - - chld_root = child; - } - - do { - child->entry = lxb_selectors_find_by_selector(selectors, root, - child->entry, list->first, - cb, ctx); - if (child->entry == NULL) { - return NULL; - } - - if (list->next == NULL) { - return chld_root; - } - - if (child->next == NULL) { - child->next = lexbor_dobject_calloc(selectors->chld); - if (child->next == NULL) { - goto failed; - } - } - - child = child->next; - list = list->next; - } - while (true); - - return chld_root; - -failed: - - selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; - - return NULL; -} - -static lxb_selectors_entry_t * -lxb_selectors_next_by_selector(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_status_t status; - lxb_dom_node_t *node = root, *base = root; - - if (entry == NULL) { - entry = lexbor_dobject_calloc(selectors->objs); - if (entry == NULL) { - selectors->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; - return NULL; - } - - entry->selector = selector; - } - - switch (selector->combinator) { - case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: - node = lxb_selectors_descendant_next(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: - node = lxb_selectors_close(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CHILD: - node = lxb_selectors_child(selectors, entry, selector, base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: - node = lxb_selectors_sibling(selectors, entry, selector, base); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: - node = lxb_selectors_following(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CELL: - default: - selectors->status = LXB_STATUS_ERROR; - return NULL; - } - - status = lxb_selectors_find_by(selectors, entry, root, node, selector, - cb, ctx); - if (status != LXB_STATUS_OK) { - return NULL; - } - - return entry; -} - -static lxb_status_t -lxb_selectors_find_by(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_dom_node_t *root, lxb_dom_node_t *node, - lxb_css_selector_t *selector, lxb_selectors_cb_f cb, void *ctx) -{ - lxb_status_t status; - lxb_selectors_entry_t *next; - lxb_dom_node_t *base = root; - - do { - if (node == NULL) { - if (entry->prev == NULL) { - return LXB_STATUS_OK; - } - - do { - entry = entry->prev; - selector = entry->selector; - } - while (entry->prev != NULL - && selector->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE); - - if (selector->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { - return LXB_STATUS_OK; - } - - node = entry->node; - base = (entry->prev != NULL) ? entry->prev->node : root; - - goto next; - } - - if (selector->next == NULL) { - status = cb(node, selector->list->specificity, ctx); - if (status != LXB_STATUS_OK) { - if (status == LXB_STATUS_STOP) { - return LXB_STATUS_OK; - } - - return status; - } - - if (selector->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { - while (entry->prev != NULL - && entry->selector->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) - { - entry = entry->prev; - } - - selector = entry->selector; - node = entry->node; - } - - base = (entry->prev != NULL) ? entry->prev->node : root; - - goto next; - } - - base = node; - entry->node = node; - - if (entry->next == NULL) { - next = lexbor_dobject_calloc(selectors->objs); - if (next == NULL) { - return LXB_STATUS_ERROR_MEMORY_ALLOCATION; - } - - next->selector = selector->next; - next->prev = entry; - entry->next = next; - entry = next; - } - else { - entry = entry->next; - } - - selector = entry->selector; - - switch (selector->combinator) { - case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: - node = lxb_selectors_descendant_next(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: - node = lxb_selectors_close(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CHILD: - node = lxb_selectors_child(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: - node = lxb_selectors_sibling(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: - node = lxb_selectors_following(selectors, entry, - selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CELL: - default: - return LXB_STATUS_ERROR; - } - - continue; - - next: - - switch (selector->combinator) { - case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: - node = lxb_selectors_descendant_next(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: - node = lxb_selectors_close(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CHILD: - node = lxb_selectors_child_next(selectors, entry, selector, - base, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: - node = NULL; - break; - - case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: - node = lxb_selectors_following(selectors, entry, - selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CELL: - default: - return LXB_STATUS_ERROR; - } - } - while (true); - - return LXB_STATUS_OK; -} - -static lxb_status_t -lxb_selectors_find_by_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *node, - lxb_css_selector_t *selector, - lxb_selectors_cb_f cb, void *ctx) -{ - lxb_status_t status; - lxb_dom_node_t *base = node; - lxb_selectors_entry_t *entry; - lxb_css_selector_combinator_t combinator; - - entry = lexbor_dobject_calloc(selectors->objs); - if (entry == NULL) { - return LXB_STATUS_ERROR_MEMORY_ALLOCATION; - } - - entry->selector = selector; - combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; - - do { - entry->node = node; - entry->id = 0; - - selector = entry->selector; - - switch (combinator) { - case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: - node = lxb_selectors_descendant_reverse(selectors, entry, - selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: - node = lxb_selectors_close(selectors, entry, selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CHILD: - node = node->parent; - - if (node == NULL || node->type != LXB_DOM_NODE_TYPE_ELEMENT - || !lxb_selectors_match(selectors, entry, selector, node)) - { - return LXB_STATUS_OK; - } - - break; - - case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: - node = lxb_selectors_sibling_reverse(selectors, entry, - selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: - node = lxb_selectors_following_reverse(selectors, entry, - selector, node); - break; - - case LXB_CSS_SELECTOR_COMBINATOR_CELL: - default: - return LXB_STATUS_ERROR; - } - - if (node == NULL) { - return LXB_STATUS_OK; - } - - if (selector->prev == NULL) { - status = cb(base, selector->list->specificity, ctx); - if (status != LXB_STATUS_OK) { - if (status == LXB_STATUS_STOP) { - return LXB_STATUS_OK; - } - - return status; - } - - return LXB_STATUS_OK; - } - - combinator = selector->combinator; - entry->selector = selector->prev; - } - while (true); - - return LXB_STATUS_OK; -} - -static bool -lxb_selectors_match(lxb_selectors_t *selectors, lxb_selectors_entry_t *entry, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - bool res, ins; - const lexbor_str_t *trg, *src; - lxb_tag_id_t tag_id; - lxb_dom_attr_t *dom_attr; - lxb_dom_element_t *element; - const lxb_dom_attr_data_t *attr_data; - lxb_css_selector_attribute_t *attr; - - static const lexbor_str_t lxb_blank_str = { - .data = (lxb_char_t *) "", - .length = 0 - }; - - switch (selector->type) { - case LXB_CSS_SELECTOR_TYPE_ANY: - return true; - - case LXB_CSS_SELECTOR_TYPE_ELEMENT: - if (entry->id == 0) { - tag_id = lxb_tag_id_by_name(node->owner_document->tags, - selector->name.data, selector->name.length); - if (tag_id == LXB_TAG__UNDEF) { - return false; - } - - entry->id = tag_id; - } - - if (node->local_name == entry->id) { - return true; - } - - break; - - case LXB_CSS_SELECTOR_TYPE_ID: - element = lxb_dom_interface_element(node); - - if (element->attr_id == NULL || element->attr_id->value == NULL) { - return false; - } - - trg = element->attr_id->value; - src = &selector->name; - - if (trg->length == src->length - && lexbor_str_data_ncasecmp(trg->data, src->data, src->length)) - { - return true; - } - - return false; - - case LXB_CSS_SELECTOR_TYPE_CLASS: - element = lxb_dom_interface_element(node); - - if (element->attr_class == NULL || element->attr_class->value == NULL) { - return false; - } - - return lxb_selectors_match_class(element->attr_class->value, - &selector->name, true); - - case LXB_CSS_SELECTOR_TYPE_ATTRIBUTE: - element = lxb_dom_interface_element(node); - attr = &selector->u.attribute; - - if (entry->id == 0) { - attr_data = lxb_dom_attr_data_by_local_name(node->owner_document->attrs, - selector->name.data, selector->name.length); - if (attr_data == NULL) { - return false; - } - - entry->id = attr_data->attr_id; - } - - dom_attr = lxb_dom_element_attr_by_id(element, entry->id); - if (dom_attr == NULL) { - return false; - } - - trg = dom_attr->value; - src = &attr->value; - - if (src->data == NULL) { - return true; - } - - if (trg == NULL) { - trg = &lxb_blank_str; - } - - ins = attr->modifier == LXB_CSS_SELECTOR_MODIFIER_I; - - switch (attr->match) { - case LXB_CSS_SELECTOR_MATCH_EQUAL: /* = */ - if (trg->length == src->length) { - if (ins) { - return lexbor_str_data_ncasecmp(trg->data, src->data, - src->length); - } - - return lexbor_str_data_ncmp(trg->data, src->data, - src->length); - } - - return false; - - case LXB_CSS_SELECTOR_MATCH_INCLUDE: /* ~= */ - return lxb_selectors_match_class(trg, src, ins); - - case LXB_CSS_SELECTOR_MATCH_DASH: /* |= */ - if (trg->length == src->length) { - if (ins) { - return lexbor_str_data_ncasecmp(trg->data, src->data, - src->length); - } - - return lexbor_str_data_ncmp(trg->data, src->data, - src->length); - } - - if (trg->length > src->length) { - if (ins) { - res = lexbor_str_data_ncasecmp(trg->data, - src->data, src->length); - } - else { - res = lexbor_str_data_ncmp(trg->data, - src->data, src->length); - } - - if (res && trg->data[src->length] == '-') { - return true; - } - } - - return false; - - case LXB_CSS_SELECTOR_MATCH_PREFIX: /* ^= */ - if (src->length != 0 && trg->length >= src->length) { - if (ins) { - return lexbor_str_data_ncasecmp(trg->data, src->data, - src->length); - } - - return lexbor_str_data_ncmp(trg->data, src->data, - src->length); - } - - return false; - - case LXB_CSS_SELECTOR_MATCH_SUFFIX: /* $= */ - if (src->length != 0 && trg->length >= src->length) { - size_t dif = trg->length - src->length; - - if (ins) { - return lexbor_str_data_ncasecmp(trg->data + dif, - src->data, src->length); - } - - return lexbor_str_data_ncmp(trg->data + dif, src->data, - src->length); - } - - return false; - - case LXB_CSS_SELECTOR_MATCH_SUBSTRING: /* *= */ - if (src->length == 0) { - return false; - } - - if (ins) { - return lexbor_str_data_ncasecmp_contain(trg->data, trg->length, - src->data, src->length); - } - - return lexbor_str_data_ncmp_contain(trg->data, trg->length, - src->data, src->length); - - default: - return false; - } - - case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS: - return lxb_selectors_pseudo_class(selector, node); - - case LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION: - return lxb_selectors_pseudo_class_function(selectors, selector, - node, entry); - - case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT: - return lxb_selectors_pseudo_element(selectors, selector, node); - - case LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION: - return lxb_selectors_pseudo_element_function(selectors, selector, node); - - default: - break; - } - - return false; -} - -static bool -lxb_selectors_match_class(const lexbor_str_t *target, const lexbor_str_t *src, - bool quirks) -{ - lxb_char_t chr; - - if (target->length < src->length) { - return false; - } - - bool is_it = false; - - const lxb_char_t *data = target->data; - const lxb_char_t *pos = data; - const lxb_char_t *end = data + target->length; - - for (; data < end; data++) { - chr = *data; - - if (lexbor_utils_whitespace(chr, ==, ||)) { - - if ((size_t) (data - pos) == src->length) { - if (quirks) { - is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length); - } - else { - is_it = lexbor_str_data_ncmp(pos, src->data, src->length); - } - - if (is_it) { - return true; - } - } - - if ((size_t) (end - data) < src->length) { - return false; - } - - pos = data + 1; - } - } - - if ((size_t) (end - pos) == src->length && src->length != 0) { - if (quirks) { - is_it = lexbor_str_data_ncasecmp(pos, src->data, src->length); - } - else { - is_it = lexbor_str_data_ncmp(pos, src->data, src->length); - } - } - - return is_it; -} - -static bool -lxb_selectors_pseudo_class(lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - lexbor_str_t *str; - lxb_dom_attr_t *attr; - lxb_dom_node_t *root; - lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo; - - static const lxb_char_t checkbox[] = "checkbox"; - static const size_t checkbox_length = sizeof(checkbox) / sizeof(lxb_char_t) - 1; - - static const lxb_char_t radio[] = "radio"; - static const size_t radio_length = sizeof(radio) / sizeof(lxb_char_t) - 1; - - switch (pseudo->type) { - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE: - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_ACTIVE); - return attr != NULL; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK: - if(node->local_name == LXB_TAG_A || - node->local_name == LXB_TAG_AREA || - node->local_name == LXB_TAG_MAP) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_HREF); - return attr != NULL; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK: - return lxb_dom_node_is_empty(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED: - if (node->local_name == LXB_TAG_INPUT) { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_TYPE); - if (attr == NULL) { - return false; - } - - if (attr->value == NULL) { - return false; - } - - str = attr->value; - - if(str->length == 8) { - if (lexbor_str_data_ncasecmp(checkbox, str->data, checkbox_length)) { - goto check; - } - } - else if(str->length == 5) { - if (lexbor_str_data_ncasecmp(radio, str->data, radio_length)) { - goto check; - } - } - } - else if(node->local_name == LXB_TAG_OPTION) { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_SELECTED); - if (attr != NULL) { - return true; - } - } - else if(node->local_name >= LXB_TAG__LAST_ENTRY) { - goto check; - } - - return false; - - check: - - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_CHECKED); - if (attr != NULL) { - return true; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT: - case LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT: - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED: - return lxb_selectors_pseudo_class_disabled(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY: - root = node; - node = node->first_child; - - while (node != NULL) { - if (node->local_name != LXB_TAG__EM_COMMENT) { - return false; - } - - if (node->first_child != NULL) { - node = node->first_child; - } - else { - while (node != root && node->next == NULL) { - node = node->parent; - } - - if (node == root) { - break; - } - - node = node->next; - } - } - - return true; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED: - return !lxb_selectors_pseudo_class_disabled(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD: - return lxb_selectors_pseudo_class_first_child(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE: - return lxb_selectors_pseudo_class_first_of_type(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS: - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_FOCUS); - return attr != NULL; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER: - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_HOVER); - return attr != NULL; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD: - return lxb_selectors_pseudo_class_last_child(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE: - return lxb_selectors_pseudo_class_last_of_type(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK: - if (node->local_name == LXB_TAG_A - || node->local_name == LXB_TAG_AREA - || node->local_name == LXB_TAG_LINK) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_HREF); - return attr != NULL; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD: - return lxb_selectors_pseudo_class_first_child(node) - && lxb_selectors_pseudo_class_last_child(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE: - return lxb_selectors_pseudo_class_first_of_type(node) - && lxb_selectors_pseudo_class_last_of_type(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL: - if (node->local_name == LXB_TAG_INPUT - || node->local_name == LXB_TAG_SELECT - || node->local_name == LXB_TAG_TEXTAREA) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_REQUIRED); - return attr == NULL; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN: - if (node->local_name == LXB_TAG_INPUT - || node->local_name == LXB_TAG_TEXTAREA) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_PLACEHOLDER); - return attr != NULL; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY: - return !lxb_selectors_pseudo_class_read_write(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE: - return lxb_selectors_pseudo_class_read_write(node); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED: - if (node->local_name == LXB_TAG_INPUT - || node->local_name == LXB_TAG_SELECT - || node->local_name == LXB_TAG_TEXTAREA) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_REQUIRED); - return attr != NULL; - } - - return false; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT: - return lxb_dom_document_root(node->owner_document) == node; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING: - break; - } - - return false; -} - -static bool -lxb_selectors_pseudo_class_function(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, - lxb_dom_node_t *node, - lxb_selectors_entry_t *entry) -{ - size_t index; - bool found = false; - lxb_dom_node_t *base; - lxb_css_selector_anb_of_t *anb; - lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo; - - switch (pseudo->type) { - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: - entry->child = lxb_selectors_next(selectors, node, entry->child, - pseudo->data, - lxb_selectors_first_match, &found); - if (entry->child == NULL) { - return false; - } - - return found; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: - entry->child = lxb_selectors_next(selectors, node, entry->child, - pseudo->data, - lxb_selectors_first_match, &found); - if (entry->child == NULL) { - return false; - } - - return found; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: - entry->child = lxb_selectors_next(selectors, node, entry->child, - pseudo->data, - lxb_selectors_first_match, &found); - if (entry->child == NULL) { - return false; - } - - return found; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: - entry->child = lxb_selectors_next(selectors, node, entry->child, - pseudo->data, - lxb_selectors_first_match, &found); - if (entry->child == NULL) { - return false; - } - - return !found; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: - index = 0; - found = false; - anb = selector->u.pseudo.data; - - if (anb->of != NULL) { - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - entry->child = lxb_selectors_current(selectors, node, entry->child, - anb->of, lxb_selectors_first_match, - &found); - if (entry->child == NULL) { - return false; - } - - if (found) { - index++; - } - - found = false; - } - - node = node->prev; - } - } - else { - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - index++; - } - - node = node->prev; - } - } - - return lxb_selectors_anb_calc(anb, index); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: - index = 0; - found = false; - anb = selector->u.pseudo.data; - - if (anb->of != NULL) { - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - entry->child = lxb_selectors_current(selectors, node, entry->child, - anb->of, lxb_selectors_first_match, - &found); - if (entry->child == NULL) { - return false; - } - - if (found) { - index++; - } - - found = false; - } - - node = node->next; - } - } - else { - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - index++; - } - - node = node->next; - } - } - - return lxb_selectors_anb_calc(anb, index); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: - break; - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: - index = 0; - found = false; - anb = selector->u.pseudo.data; - base = node; - - while (node != NULL) { - if(node->local_name == base->local_name - && node->ns == base->ns) - { - index++; - } - - node = node->next; - } - - return lxb_selectors_anb_calc(anb, index); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: - index = 0; - found = false; - anb = selector->u.pseudo.data; - base = node; - - while (node != NULL) { - if(node->local_name == base->local_name - && node->ns == base->ns) - { - index++; - } - - node = node->prev; - } - - return lxb_selectors_anb_calc(anb, index); - - case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: - entry->child = lxb_selectors_next(selectors, node, entry->child, - pseudo->data, - lxb_selectors_first_match, &found); - if (entry->child == NULL) { - return false; - } - - return found; - } - - return false; -} - -static bool -lxb_selectors_pseudo_element(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - lxb_css_selector_pseudo_t *pseudo = &selector->u.pseudo; - - switch (pseudo->type) { - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR: - case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT: - break; - } - - return false; -} - -static bool -lxb_selectors_pseudo_element_function(lxb_selectors_t *selectors, - lxb_css_selector_t *selector, lxb_dom_node_t *node) -{ - return false; -} - -static bool -lxb_selectors_pseudo_class_disabled(lxb_dom_node_t *node) -{ - lxb_dom_attr_t *attr; - uintptr_t tag_id = node->local_name; - - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_DISABLED); - if (attr == NULL) { - return false; - } - - if (tag_id == LXB_TAG_BUTTON || tag_id == LXB_TAG_INPUT || - tag_id == LXB_TAG_SELECT || tag_id == LXB_TAG_TEXTAREA || - tag_id >= LXB_TAG__LAST_ENTRY) - { - return true; - } - - node = node->parent; - - while (node != NULL) { - if (node->local_name == LXB_TAG_FIELDSET - && node->first_child->local_name != LXB_TAG_LEGEND) - { - return true; - } - - node = node->parent; - } - - return false; -} - -static bool -lxb_selectors_pseudo_class_first_child(lxb_dom_node_t *node) -{ - node = node->prev; - - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - return false; - } - - node = node->prev; - } - - return true; -} - -static bool -lxb_selectors_pseudo_class_first_of_type(lxb_dom_node_t *node) -{ - lxb_dom_node_t *root = node; - node = node->prev; - - while (node) { - if (node->local_name == root->local_name - && node->ns == root->ns) - { - return false; - } - - node = node->prev; - } - - return true; -} - -static bool -lxb_selectors_pseudo_class_last_child(lxb_dom_node_t *node) -{ - node = node->next; - - while (node != NULL) { - if (node->local_name != LXB_TAG__TEXT - && node->local_name != LXB_TAG__EM_COMMENT) - { - return false; - } - - node = node->next; - } - - return true; -} - -static bool -lxb_selectors_pseudo_class_last_of_type(lxb_dom_node_t *node) -{ - lxb_dom_node_t *root = node; - node = node->next; - - while (node) { - if (node->local_name == root->local_name - && node->ns == root->ns) - { - return false; - } - - node = node->next; - } - - return true; -} - -static bool -lxb_selectors_pseudo_class_read_write(lxb_dom_node_t *node) -{ - lxb_dom_attr_t *attr; - - if (node->local_name == LXB_TAG_INPUT - || node->local_name == LXB_TAG_TEXTAREA) - { - attr = lxb_dom_element_attr_by_id(lxb_dom_interface_element(node), - LXB_DOM_ATTR_READONLY); - if (attr != NULL) { - return false; - } - - return !lxb_selectors_pseudo_class_disabled(node); - } - - return false; -} - -static lxb_status_t -lxb_selectors_first_match(lxb_dom_node_t *node, - lxb_css_selector_specificity_t spec, void *ctx) -{ - *((bool *) ctx) = true; - return LXB_STATUS_STOP; -} diff --git a/ext/dom/lexbor/lexbor/selectors/selectors.h b/ext/dom/lexbor/lexbor/selectors/selectors.h deleted file mode 100644 index 66ce7b6f60089..0000000000000 --- a/ext/dom/lexbor/lexbor/selectors/selectors.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 Alexander Borisov - * - * Author: Alexander Borisov - */ - - -#ifndef LEXBOR_SELECTORS_H -#define LEXBOR_SELECTORS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "lexbor/selectors/base.h" -#include "lexbor/dom/dom.h" -#include "lexbor/css/selectors/selectors.h" - - -typedef lxb_status_t -(*lxb_selectors_cb_f)(lxb_dom_node_t *node, lxb_css_selector_specificity_t spec, - void *ctx); - -typedef struct lxb_selectors_entry lxb_selectors_entry_t; -typedef struct lxb_selectors_entry_child lxb_selectors_entry_child_t; - -typedef struct { - lexbor_dobject_t *objs; - lexbor_dobject_t *chld; - - lxb_status_t status; -} -lxb_selectors_t; - -struct lxb_selectors_entry { - uintptr_t id; - lxb_css_selector_t *selector; - lxb_dom_node_t *node; - lxb_selectors_entry_t *next; - lxb_selectors_entry_t *prev; - lxb_selectors_entry_child_t *child; -}; - -struct lxb_selectors_entry_child { - lxb_selectors_entry_t *entry; - lxb_selectors_entry_child_t *next; -}; - - -LXB_API lxb_selectors_t * -lxb_selectors_create(void); - -LXB_API lxb_status_t -lxb_selectors_init(lxb_selectors_t *selectors); - -LXB_API void -lxb_selectors_clean(lxb_selectors_t *selectors); - -LXB_API lxb_selectors_t * -lxb_selectors_destroy(lxb_selectors_t *selectors, bool self_destroy); - -LXB_API lxb_status_t -lxb_selectors_find(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_css_selector_list_t *list, lxb_selectors_cb_f cb, void *ctx); - -LXB_API lxb_status_t -lxb_selectors_find_reverse(lxb_selectors_t *selectors, lxb_dom_node_t *root, - lxb_css_selector_list_t *list, - lxb_selectors_cb_f cb, void *ctx); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LEXBOR_SELECTORS_H */ diff --git a/ext/dom/lexbor/lexbor/tag/tag.c b/ext/dom/lexbor/lexbor/tag/tag.c old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/lexbor/tag/tag.h b/ext/dom/lexbor/lexbor/tag/tag.h old mode 100755 new mode 100644 diff --git a/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch index 7549403ea09b4..2174b670bb356 100644 --- a/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch +++ b/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch @@ -1,7 +1,7 @@ -From 9d60c0fda0b51e9374a234c48df36130d2c988ee Mon Sep 17 00:00:00 2001 +From 7c7c35d8ea9f65f081564b3ad1bfe9f0db33dd69 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:08:59 +0200 -Subject: [PATCH] Expose line and column information for use in PHP +Subject: [PATCH 1/6] Expose line and column information for use in PHP --- source/lexbor/dom/interfaces/node.h | 2 ++ @@ -15,7 +15,7 @@ Subject: [PATCH] Expose line and column information for use in PHP 8 files changed, 48 insertions(+), 5 deletions(-) diff --git a/source/lexbor/dom/interfaces/node.h b/source/lexbor/dom/interfaces/node.h -index 4a10197..ff9c924 100755 +index c37b790..8ac218b 100644 --- a/source/lexbor/dom/interfaces/node.h +++ b/source/lexbor/dom/interfaces/node.h @@ -58,6 +58,8 @@ struct lxb_dom_node { @@ -28,7 +28,7 @@ index 4a10197..ff9c924 100755 LXB_DOM_NODE_USER_VARIABLES #endif /* LXB_DOM_NODE_USER_VARIABLES */ diff --git a/source/lexbor/html/token.h b/source/lexbor/html/token.h -index 79accd0..0b7f4fd 100755 +index 79accd0..0b7f4fd 100644 --- a/source/lexbor/html/token.h +++ b/source/lexbor/html/token.h @@ -33,6 +33,8 @@ enum lxb_html_token_type { @@ -41,7 +41,7 @@ index 79accd0..0b7f4fd 100755 const lxb_char_t *text_start; const lxb_char_t *text_end; diff --git a/source/lexbor/html/tokenizer.c b/source/lexbor/html/tokenizer.c -index 741bced..0bd9aec 100755 +index 741bced..0bd9aec 100644 --- a/source/lexbor/html/tokenizer.c +++ b/source/lexbor/html/tokenizer.c @@ -91,6 +91,7 @@ lxb_html_tokenizer_init(lxb_html_tokenizer_t *tkz) @@ -90,7 +90,7 @@ index 741bced..0bd9aec 100755 return tkz->status; diff --git a/source/lexbor/html/tokenizer.h b/source/lexbor/html/tokenizer.h -index ba9602f..74bb55e 100755 +index ba9602f..74bb55e 100644 --- a/source/lexbor/html/tokenizer.h +++ b/source/lexbor/html/tokenizer.h @@ -73,6 +73,8 @@ struct lxb_html_tokenizer { @@ -103,7 +103,7 @@ index ba9602f..74bb55e 100755 /* Entities */ const lexbor_sbst_entry_static_t *entity; diff --git a/source/lexbor/html/tokenizer/state.h b/source/lexbor/html/tokenizer/state.h -index 0892846..77b86ac 100755 +index 0892846..77b86ac 100644 --- a/source/lexbor/html/tokenizer/state.h +++ b/source/lexbor/html/tokenizer/state.h @@ -90,6 +90,8 @@ extern "C" { @@ -116,7 +116,7 @@ index 0892846..77b86ac 100755 while (0) diff --git a/source/lexbor/html/tree.c b/source/lexbor/html/tree.c -index 0f067e4..bdec6a5 100755 +index 0f067e4..bdec6a5 100644 --- a/source/lexbor/html/tree.c +++ b/source/lexbor/html/tree.c @@ -434,6 +434,9 @@ lxb_html_tree_create_element_for_token(lxb_html_tree_t *tree, @@ -152,7 +152,7 @@ index 0f067e4..bdec6a5 100755 tree->document->dom_document.text); if (tree->status != LXB_STATUS_OK) { diff --git a/source/lexbor/html/tree/error.c b/source/lexbor/html/tree/error.c -index e6e43f4..88ad8c4 100755 +index e6e43f4..88ad8c4 100644 --- a/source/lexbor/html/tree/error.c +++ b/source/lexbor/html/tree/error.c @@ -21,8 +21,9 @@ lxb_html_tree_error_add(lexbor_array_obj_t *parse_errors, @@ -168,7 +168,7 @@ index e6e43f4..88ad8c4 100755 return entry; } diff --git a/source/lexbor/html/tree/error.h b/source/lexbor/html/tree/error.h -index 2fd06cb..ed1859f 100755 +index 2fd06cb..ed1859f 100644 --- a/source/lexbor/html/tree/error.h +++ b/source/lexbor/html/tree/error.h @@ -97,8 +97,9 @@ lxb_html_tree_error_id_t; @@ -184,5 +184,5 @@ index 2fd06cb..ed1859f 100755 lxb_html_tree_error_t; -- -2.43.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/0001-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch similarity index 90% rename from ext/dom/lexbor/patches/0001-Track-implied-added-nodes-for-options-use-in-PHP.patch rename to ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch index c3ccc51df403f..b3f56201040fb 100644 --- a/ext/dom/lexbor/patches/0001-Track-implied-added-nodes-for-options-use-in-PHP.patch +++ b/ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch @@ -1,7 +1,7 @@ -From 7f04b3dc1501458e7f5cd0d6e6cd05db524ae6ae Mon Sep 17 00:00:00 2001 +From ce1cb529bb5415e4ea17332731c20d2c78adc601 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Aug 2023 20:18:51 +0200 -Subject: [PATCH] Track implied added nodes for options use in PHP +Subject: [PATCH 2/6] Track implied added nodes for options use in PHP --- source/lexbor/html/tree.h | 3 +++ @@ -11,7 +11,7 @@ Subject: [PATCH] Track implied added nodes for options use in PHP 4 files changed, 8 insertions(+) diff --git a/source/lexbor/html/tree.h b/source/lexbor/html/tree.h -index 2a43f8b..d964f01 100755 +index 2a43f8b..d964f01 100644 --- a/source/lexbor/html/tree.h +++ b/source/lexbor/html/tree.h @@ -55,6 +55,9 @@ struct lxb_html_tree { @@ -25,7 +25,7 @@ index 2a43f8b..d964f01 100755 lxb_html_tree_insertion_mode_f mode; lxb_html_tree_insertion_mode_f original_mode; diff --git a/source/lexbor/html/tree/insertion_mode/after_head.c b/source/lexbor/html/tree/insertion_mode/after_head.c -index ad551b5..1448654 100755 +index ad551b5..1448654 100644 --- a/source/lexbor/html/tree/insertion_mode/after_head.c +++ b/source/lexbor/html/tree/insertion_mode/after_head.c @@ -71,6 +71,7 @@ lxb_html_tree_insertion_mode_after_head_open(lxb_html_tree_t *tree, @@ -37,7 +37,7 @@ index ad551b5..1448654 100755 tree->mode = lxb_html_tree_insertion_mode_in_body; diff --git a/source/lexbor/html/tree/insertion_mode/before_head.c b/source/lexbor/html/tree/insertion_mode/before_head.c -index 14621f2..cd2ac2a 100755 +index 14621f2..cd2ac2a 100644 --- a/source/lexbor/html/tree/insertion_mode/before_head.c +++ b/source/lexbor/html/tree/insertion_mode/before_head.c @@ -67,6 +67,8 @@ lxb_html_tree_insertion_mode_before_head_open(lxb_html_tree_t *tree, @@ -50,7 +50,7 @@ index 14621f2..cd2ac2a 100755 break; diff --git a/source/lexbor/html/tree/insertion_mode/before_html.c b/source/lexbor/html/tree/insertion_mode/before_html.c -index ed5e367..b078ac5 100755 +index ed5e367..b078ac5 100644 --- a/source/lexbor/html/tree/insertion_mode/before_html.c +++ b/source/lexbor/html/tree/insertion_mode/before_html.c @@ -79,6 +79,8 @@ lxb_html_tree_insertion_mode_before_html_open(lxb_html_tree_t *tree, @@ -63,5 +63,5 @@ index ed5e367..b078ac5 100755 break; -- -2.41.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/0001-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch similarity index 95% rename from ext/dom/lexbor/patches/0001-Patch-utilities-and-data-structure-to-be-able-to-gen.patch rename to ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch index 75135dcc447dd..5b32b3337d083 100644 --- a/ext/dom/lexbor/patches/0001-Patch-utilities-and-data-structure-to-be-able-to-gen.patch +++ b/ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch @@ -1,7 +1,7 @@ -From d9c670e2a58fbfd3670a5ca69cd583b3573e77ef Mon Sep 17 00:00:00 2001 +From 0514a1cb2f45ab6dd814118780d56a713f4925a2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 24 Aug 2023 22:57:48 +0200 -Subject: [PATCH] Patch utilities and data structure to be able to generate +Subject: [PATCH 3/6] Patch utilities and data structure to be able to generate smaller lookup tables Changed the generation script to check if everything fits in 32-bits. @@ -15,7 +15,7 @@ tables in size. 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source/lexbor/core/shs.h b/source/lexbor/core/shs.h -index 7a63a07..c84dfaa 100755 +index 7a63a07..c84dfaa 100644 --- a/source/lexbor/core/shs.h +++ b/source/lexbor/core/shs.h @@ -27,9 +27,9 @@ lexbor_shs_entry_t; @@ -98,5 +98,5 @@ index 3e75812..2370c66 100755 result.append("};") -- -2.41.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/0001-Remove-unused-upper-case-tag-static-data.patch b/ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch similarity index 91% rename from ext/dom/lexbor/patches/0001-Remove-unused-upper-case-tag-static-data.patch rename to ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch index 529649a369c8b..a022e16f36e3b 100644 --- a/ext/dom/lexbor/patches/0001-Remove-unused-upper-case-tag-static-data.patch +++ b/ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch @@ -1,7 +1,7 @@ -From 24b52ec63eb55adb1c039e58dd3e1156f01083b2 Mon Sep 17 00:00:00 2001 +From 01aad1074657586677f05ac1998da2158c57ee74 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:26:47 +0100 -Subject: [PATCH 1/2] Remove unused upper case tag static data +Subject: [PATCH 4/6] Remove unused upper case tag static data --- source/lexbor/tag/res.h | 2 ++ @@ -29,7 +29,7 @@ index c7190c5..4ad1f37 100644 static const lexbor_shs_entry_t lxb_tag_res_shs_data_default[] = { diff --git a/source/lexbor/tag/tag.c b/source/lexbor/tag/tag.c -index f8fcdf0..0571957 100755 +index f8fcdf0..0571957 100644 --- a/source/lexbor/tag/tag.c +++ b/source/lexbor/tag/tag.c @@ -92,6 +92,7 @@ lxb_tag_data_by_name(lexbor_hash_t *hash, const lxb_char_t *name, size_t len) @@ -49,5 +49,5 @@ index f8fcdf0..0571957 100755 /* * No inline functions for ABI. -- -2.43.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/0001-Shrink-size-of-static-binary-search-tree.patch b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch similarity index 93% rename from ext/dom/lexbor/patches/0001-Shrink-size-of-static-binary-search-tree.patch rename to ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch index 3c0af9f7a721f..dfb0c0a980ebb 100644 --- a/ext/dom/lexbor/patches/0001-Shrink-size-of-static-binary-search-tree.patch +++ b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch @@ -1,7 +1,7 @@ -From 7fde66f32dcfbdc5df97fbffe411c0d7fd60fa50 Mon Sep 17 00:00:00 2001 +From 16daa8e860e393ff39613b908550b0982a2210f2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 29 Nov 2023 21:29:31 +0100 -Subject: [PATCH 2/2] Shrink size of static binary search tree +Subject: [PATCH 5/6] Shrink size of static binary search tree This also makes it more efficient on the data cache. --- @@ -13,7 +13,7 @@ This also makes it more efficient on the data cache. 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/lexbor/core/sbst.h b/source/lexbor/core/sbst.h -index b0fbc54..40e0e91 100755 +index b0fbc54..40e0e91 100644 --- a/source/lexbor/core/sbst.h +++ b/source/lexbor/core/sbst.h @@ -19,12 +19,12 @@ extern "C" { @@ -35,7 +35,7 @@ index b0fbc54..40e0e91 100755 lexbor_sbst_entry_static_t; diff --git a/source/lexbor/html/tokenizer/state.c b/source/lexbor/html/tokenizer/state.c -index 70ca391..2f3414f 100755 +index 70ca391..2f3414f 100644 --- a/source/lexbor/html/tokenizer/state.c +++ b/source/lexbor/html/tokenizer/state.c @@ -1815,7 +1815,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, @@ -48,7 +48,7 @@ index 70ca391..2f3414f 100755 tkz->entity_match = entry; } diff --git a/utils/lexbor/html/tmp/tokenizer_res.h b/utils/lexbor/html/tmp/tokenizer_res.h -index b3701d5..73ab66e 100755 +index b3701d5..73ab66e 100644 --- a/utils/lexbor/html/tmp/tokenizer_res.h +++ b/utils/lexbor/html/tmp/tokenizer_res.h @@ -6,7 +6,7 @@ @@ -98,7 +98,7 @@ index ee7dcb4..7cd1335 100755 + "../../../source/lexbor/html/tokenizer/res.h", "data/entities.json"); diff --git a/utils/lexbor/lexbor/LXB.py b/utils/lexbor/lexbor/LXB.py -index 3e75812..b068ea3 100755 +index 2370c66..c41e645 100755 --- a/utils/lexbor/lexbor/LXB.py +++ b/utils/lexbor/lexbor/LXB.py @@ -27,7 +27,7 @@ class Temp: @@ -111,5 +111,5 @@ index 3e75812..b068ea3 100755 fh.close() -- -2.43.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/0001-Patch-out-CSS-parser.patch b/ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch similarity index 70% rename from ext/dom/lexbor/patches/0001-Patch-out-CSS-parser.patch rename to ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch index 9928b5f32e49a..500daf5026aa6 100644 --- a/ext/dom/lexbor/patches/0001-Patch-out-CSS-parser.patch +++ b/ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch @@ -1,38 +1,50 @@ -From c19debb4d26f731b39860f27073b69927aa611f0 Mon Sep 17 00:00:00 2001 +From 168dad55b6278cd45e0f0b2aed802ce9bace3274 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Wed, 23 Aug 2023 21:48:12 +0200 -Subject: [PATCH] Patch out CSS parser +Date: Sun, 7 Jan 2024 21:59:28 +0100 +Subject: [PATCH 6/6] Patch out unused CSS style code --- source/lexbor/css/rule.h | 2 ++ - source/lexbor/html/interfaces/document.c | 16 ++++++++++ + source/lexbor/html/interfaces/document.c | 29 +++++++++++++++++++ + source/lexbor/html/interfaces/document.h | 2 -- source/lexbor/html/interfaces/element.c | 29 +++++++++++++++++++ source/lexbor/html/interfaces/style_element.c | 6 ++++ - 4 files changed, 53 insertions(+) + 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/source/lexbor/css/rule.h b/source/lexbor/css/rule.h -index 7cc4f0b..f68491e 100644 +index 7cc4f0b..bd191f9 100644 --- a/source/lexbor/css/rule.h +++ b/source/lexbor/css/rule.h -@@ -344,7 +344,9 @@ lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) +@@ -339,6 +339,7 @@ lxb_css_rule_ref_dec(lxb_css_rule_t *rule) + lxb_inline void + lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) + { ++#if 0 + if (rule->ref_count > 0) { + rule->ref_count--; } - +@@ -346,6 +347,7 @@ lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) if (rule->ref_count == 0) { -+#if 0 (void) lxb_css_rule_destroy(rule, true); -+#endif } ++#endif } + lxb_inline void diff --git a/source/lexbor/html/interfaces/document.c b/source/lexbor/html/interfaces/document.c -index 73184f0..b4de5b8 100755 +index bd3c02b..aa305a9 100644 --- a/source/lexbor/html/interfaces/document.c +++ b/source/lexbor/html/interfaces/document.c -@@ -72,8 +72,10 @@ static lxb_status_t - lxb_html_document_style_cb(lxb_dom_node_t *node, - lxb_css_selector_specificity_t spec, void *ctx); +@@ -58,6 +58,7 @@ static lxb_html_document_css_custom_entry_t * + lxb_html_document_css_customs_insert(lxb_html_document_t *document, + const lxb_char_t *key, size_t length); +#if 0 + static lxb_status_t + lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, + lxb_css_selector_specificity_t spec, +@@ -74,6 +75,7 @@ lxb_html_document_style_cb(lxb_dom_node_t *node, + static lxb_status_t lxb_html_document_done(lxb_html_document_t *document); +#endif @@ -96,7 +108,7 @@ index 73184f0..b4de5b8 100755 size_t length; lxb_css_stylesheet_t *sst; lxb_html_document_css_t *css = &document->css; -@@ -586,6 +597,7 @@ lxb_html_document_stylesheet_destroy_all(lxb_html_document_t *document, +@@ -586,29 +597,37 @@ lxb_html_document_stylesheet_destroy_all(lxb_html_document_t *document, (void) lxb_css_stylesheet_destroy(sst, destroy_memory); } @@ -104,7 +116,71 @@ index 73184f0..b4de5b8 100755 } lxb_status_t -@@ -849,6 +861,7 @@ lxb_html_document_parser_prepare(lxb_html_document_t *document) + lxb_html_document_style_attach(lxb_html_document_t *document, + lxb_css_rule_style_t *style) + { ++#if 0 + lxb_html_document_css_t *css = &document->css; + + return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), + style->selector, lxb_html_document_style_cb, style); ++#endif ++ return LXB_STATUS_OK; + } + + lxb_status_t + lxb_html_document_style_remove(lxb_html_document_t *document, + lxb_css_rule_style_t *style) + { ++#if 0 + lxb_html_document_css_t *css = &document->css; + + return lxb_selectors_find(css->selectors, lxb_dom_interface_node(document), + style->selector, + lxb_html_document_style_remove_by_rule_cb, style); ++#endif ++ return LXB_STATUS_OK; + } + ++#if 0 + static lxb_status_t + lxb_html_document_style_remove_by_rule_cb(lxb_dom_node_t *node, + lxb_css_selector_specificity_t spec, +@@ -646,20 +665,25 @@ lxb_html_document_style_remove_avl_cb(lexbor_avl_t *avl, + style, context->list); + return LXB_STATUS_OK; + } ++#endif + + lxb_status_t + lxb_html_document_style_attach_by_element(lxb_html_document_t *document, + lxb_html_element_t *element, + lxb_css_rule_style_t *style) + { ++#if 0 + lxb_html_document_css_t *css = &document->css; + + return lxb_selectors_match_node(css->selectors, + lxb_dom_interface_node(element), + style->selector, + lxb_html_document_style_cb, style); ++#endif ++ return LXB_STATUS_OK; + } + ++#if 0 + static lxb_status_t + lxb_html_document_style_cb(lxb_dom_node_t *node, + lxb_css_selector_specificity_t spec, void *ctx) +@@ -675,6 +699,7 @@ lxb_html_document_style_cb(lxb_dom_node_t *node, + return lxb_html_element_style_list_append(lxb_html_interface_element(node), + style->declarations, spec); + } ++#endif + + lxb_html_document_t * + lxb_html_document_destroy(lxb_html_document_t *document) +@@ -851,6 +876,7 @@ lxb_html_document_parser_prepare(lxb_html_document_t *document) return LXB_STATUS_OK; } @@ -112,7 +188,7 @@ index 73184f0..b4de5b8 100755 static lxb_status_t lxb_html_document_done(lxb_html_document_t *document) { -@@ -873,6 +886,7 @@ lxb_html_document_done(lxb_html_document_t *document) +@@ -875,6 +901,7 @@ lxb_html_document_done(lxb_html_document_t *document) return LXB_STATUS_OK; } @@ -120,7 +196,7 @@ index 73184f0..b4de5b8 100755 const lxb_char_t * lxb_html_document_title(lxb_html_document_t *document, size_t *len) -@@ -960,6 +974,7 @@ lxb_html_document_import_node(lxb_html_document_t *doc, lxb_dom_node_t *node, +@@ -962,6 +989,7 @@ lxb_html_document_import_node(lxb_html_document_t *doc, lxb_dom_node_t *node, return lxb_dom_document_import_node(&doc->dom_document, node, deep); } @@ -128,7 +204,7 @@ index 73184f0..b4de5b8 100755 static lxb_status_t lxb_html_document_event_insert(lxb_dom_node_t *node) { -@@ -1231,6 +1246,7 @@ lxb_html_document_event_set_value(lxb_dom_node_t *node, +@@ -1233,6 +1261,7 @@ lxb_html_document_event_set_value(lxb_dom_node_t *node, return lxb_html_element_style_parse(lxb_html_interface_element(node), value, length); } @@ -136,8 +212,28 @@ index 73184f0..b4de5b8 100755 /* * No inline functions for ABI. +diff --git a/source/lexbor/html/interfaces/document.h b/source/lexbor/html/interfaces/document.h +index 7e8d1ea..827ff64 100644 +--- a/source/lexbor/html/interfaces/document.h ++++ b/source/lexbor/html/interfaces/document.h +@@ -20,7 +20,6 @@ extern "C" { + #include "lexbor/dom/interfaces/attr.h" + #include "lexbor/dom/interfaces/document.h" + #include "lexbor/css/css.h" +-#include "lexbor/selectors/selectors.h" + + + typedef lxb_status_t +@@ -45,7 +44,6 @@ typedef struct { + lxb_css_memory_t *memory; + lxb_css_selectors_t *css_selectors; + lxb_css_parser_t *parser; +- lxb_selectors_t *selectors; + + lexbor_avl_t *styles; + lexbor_array_t *stylesheets; diff --git a/source/lexbor/html/interfaces/element.c b/source/lexbor/html/interfaces/element.c -index 229d3d7..363040c 100755 +index 229d3d7..363040c 100644 --- a/source/lexbor/html/interfaces/element.c +++ b/source/lexbor/html/interfaces/element.c @@ -38,9 +38,11 @@ static lxb_status_t @@ -306,7 +402,7 @@ index 229d3d7..363040c 100755 } +#endif diff --git a/source/lexbor/html/interfaces/style_element.c b/source/lexbor/html/interfaces/style_element.c -index 66d55c4..9a402ef 100755 +index 66d55c4..9a402ef 100644 --- a/source/lexbor/html/interfaces/style_element.c +++ b/source/lexbor/html/interfaces/style_element.c @@ -35,7 +35,9 @@ lxb_html_style_element_interface_destroy(lxb_html_style_element_t *style_element @@ -345,5 +441,5 @@ index 66d55c4..9a402ef 100755 return css->parser->status; } -- -2.41.0 +2.44.0 diff --git a/ext/dom/lexbor/patches/README.md b/ext/dom/lexbor/patches/README.md index 09a112d8da271..d10b437818c20 100644 --- a/ext/dom/lexbor/patches/README.md +++ b/ext/dom/lexbor/patches/README.md @@ -9,19 +9,19 @@ This contains the following patch files in mailbox format. * 0001-Expose-line-and-column-information-for-use-in-PHP.patch A PHP specific patch to expose the line and column number to PHP. -* 0001-Track-implied-added-nodes-for-options-use-in-PHP.patch +* 0002-Track-implied-added-nodes-for-options-use-in-PHP.patch A PHP specific patch to track implied added nodes for options. -* 0001-Patch-out-CSS-parser.patch - A PHP specific patch to patch out the CSS parser, which is unused and hence this patch reduces the binary size. -* 0001-Patch-utilities-and-data-structure-to-be-able-to-gen.patch +* 0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch A PHP specific patch to patch utilities and data structure to be able to generate smaller lookup tables. This patch won't be upstreamed because it breaks generality of those data structures, i.e. it only works because we only use it for character encoding. -* 0001-Remove-unused-upper-case-tag-static-data.patch +* 0004-Remove-unused-upper-case-tag-static-data.patch A PHP specific patch to remove unused upper case tag static data. This shrinks the static data size. -* 0001-Shrink-size-of-static-binary-search-tree.patch +* 0005-Shrink-size-of-static-binary-search-tree.patch A PHP specific patch to shrink the size of the static binary search tree for entities. This shrinks the static data size and reduces data cache pressure. +* 0006-Patch-out-unused-CSS-style-code.patch + A PHP specific patch to remove CSS style and selector bindings from the Lexbor document. **Note** for this patch the utilities to generate the tables are also patched. Make sure to apply on a fresh Lexbor clone and run (in `lexbor/utils/encoding`): `python3 single-byte.py` and `python3 multi-byte.py` to generate the tables. @@ -31,9 +31,9 @@ This contains the following patch files in mailbox format. * cd into `ext/dom/lexbor/lexbor` * `git am -3 ../patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch` -* `git am -3 ../patches/0001-Track-implied-added-nodes-for-options-use-in-PHP.patch` -* `git am -3 ../patches/0001-Patch-out-CSS-parser.patch` -* `git am -3 ../patches/0001-Patch-utilities-and-data-structure-to-be-able-to-gen.patch` -* `git am -3 ../patches/0001-Remove-unused-upper-case-tag-static-data.patch` -* `git am -3 ../patches/0001-Shrink-size-of-static-binary-search-tree.patch` +* `git am -3 ../patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch` +* `git am -3 ../patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch` +* `git am -3 ../patches/0004-Remove-unused-upper-case-tag-static-data.patch` +* `git am -3 ../patches/0005-Shrink-size-of-static-binary-search-tree.patch` +* `git am -3 ../patches/0006-Patch-out-unused-CSS-style-code.patch` * `git reset HEAD~6` # 6 is the number of commits created by the above commands diff --git a/ext/dom/parentnode/css_selectors.c b/ext/dom/parentnode/css_selectors.c new file mode 100644 index 0000000000000..ff1d07abc5321 --- /dev/null +++ b/ext/dom/parentnode/css_selectors.c @@ -0,0 +1,282 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Niels Dossche | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#if defined(HAVE_LIBXML) && defined(HAVE_DOM) +#include "../php_dom.h" + +#include "lexbor/css/parser.h" +#include "lexbor/selectors-adapted/selectors.h" + +// TODO: optimization idea: cache the parsed selectors in an LRU fashion? + +typedef struct { + HashTable *list; + dom_object *intern; +} dom_query_selector_all_ctx; + +typedef struct { + const xmlNode *reference; + bool result; +} dom_query_selector_matches_ctx; + +static lxb_selectors_opt_t dom_quirks_opt(lxb_selectors_opt_t options, const dom_object *intern) +{ + if (intern->document != NULL && intern->document->quirks_mode) { + options |= LXB_SELECTORS_OPT_QUIRKS_MODE; + } + return options; +} + +lxb_status_t dom_query_selector_find_single_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx) +{ + xmlNodePtr *result = (xmlNodePtr *) ctx; + *result = (xmlNodePtr) node; + return LXB_STATUS_STOP; +} + +lxb_status_t dom_query_selector_find_array_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx) +{ + dom_query_selector_all_ctx *qsa_ctx = (dom_query_selector_all_ctx *) ctx; + zval object; + php_dom_create_object((xmlNodePtr) node, &object, qsa_ctx->intern); + zend_hash_next_index_insert_new(qsa_ctx->list, &object); + return LXB_STATUS_OK; +} + +lxb_status_t dom_query_selector_find_matches_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx) +{ + dom_query_selector_matches_ctx *matches_ctx = (dom_query_selector_matches_ctx *) ctx; + if (node == matches_ctx->reference) { + matches_ctx->result = true; + return LXB_STATUS_STOP; + } + return LXB_STATUS_OK; +} + +static lxb_css_selector_list_t *dom_parse_selector( + lxb_css_parser_t *parser, + lxb_selectors_t *selectors, + const zend_string *selectors_str, + lxb_selectors_opt_t options, + const dom_object *intern +) +{ + lxb_status_t status; + + memset(parser, 0, sizeof(lxb_css_parser_t)); + status = lxb_css_parser_init(parser, NULL); + ZEND_ASSERT(status == LXB_STATUS_OK); + + memset(selectors, 0, sizeof(lxb_selectors_t)); + status = lxb_selectors_init(selectors); + ZEND_ASSERT(status == LXB_STATUS_OK); + lxb_selectors_opt_set(selectors, dom_quirks_opt(options, intern)); + + lxb_css_selector_list_t *list = lxb_css_selectors_parse(parser, (const lxb_char_t *) ZSTR_VAL(selectors_str), ZSTR_LEN(selectors_str)); + if (UNEXPECTED(list == NULL)) { + size_t nr_of_messages = lexbor_array_obj_length(&parser->log->messages); + if (nr_of_messages > 0) { + lxb_css_log_message_t *msg = lexbor_array_obj_get(&parser->log->messages, 0); + char *error; + zend_spprintf(&error, 0, "Invalid selector (%.*s)", (int) msg->text.length, msg->text.data); + php_dom_throw_error_with_message(SYNTAX_ERR, error, true); + efree(error); + } else { + php_dom_throw_error_with_message(SYNTAX_ERR, "Invalid selector", true); + } + } + + return list; +} + +static lxb_status_t dom_check_css_execution_status(lxb_status_t status) +{ + if (UNEXPECTED(status != LXB_STATUS_OK && status != LXB_STATUS_STOP)) { + zend_argument_value_error(1, "contains an unsupported selector"); + return status; + } + return LXB_STATUS_OK; +} + +static void dom_selector_cleanup(lxb_css_parser_t *parser, lxb_selectors_t *selectors, lxb_css_selector_list_t *list) +{ + lxb_css_selector_list_destroy_memory(list); + lxb_selectors_destroy(selectors); + (void) lxb_css_parser_destroy(parser, false); +} + +static lxb_status_t dom_query_selector_common( + const xmlNode *root, + const dom_object *intern, + const zend_string *selectors_str, + lxb_selectors_cb_f cb, + void *ctx, + lxb_selectors_opt_t options +) +{ + lxb_status_t status; + + lxb_css_parser_t parser; + lxb_selectors_t selectors; + + lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, options, intern); + if (UNEXPECTED(list == NULL)) { + status = LXB_STATUS_ERROR; + } else { + status = lxb_selectors_find(&selectors, root, list, cb, ctx); + status = dom_check_css_execution_status(status); + } + + dom_selector_cleanup(&parser, &selectors, list); + + return status; +} + +static lxb_status_t dom_query_matches( + const xmlNode *root, + const dom_object *intern, + const zend_string *selectors_str, + void *ctx +) +{ + lxb_status_t status; + + lxb_css_parser_t parser; + lxb_selectors_t selectors; + + lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern); + if (UNEXPECTED(list == NULL)) { + status = LXB_STATUS_ERROR; + } else { + status = lxb_selectors_match_node(&selectors, root, list, dom_query_selector_find_matches_callback, ctx); + status = dom_check_css_execution_status(status); + } + + dom_selector_cleanup(&parser, &selectors, list); + + return status; +} + +static const xmlNode *dom_query_closest( + const xmlNode *root, + const dom_object *intern, + const zend_string *selectors_str +) +{ + const xmlNode *ret = NULL; + + lxb_css_parser_t parser; + lxb_selectors_t selectors; + + lxb_css_selector_list_t *list = dom_parse_selector(&parser, &selectors, selectors_str, LXB_SELECTORS_OPT_MATCH_FIRST, intern); + if (EXPECTED(list != NULL)) { + const xmlNode *current = root; + while (current != NULL) { + dom_query_selector_matches_ctx ctx = { current, false }; + lxb_status_t status = lxb_selectors_match_node(&selectors, current, list, dom_query_selector_find_matches_callback, &ctx); + status = dom_check_css_execution_status(status); + if (UNEXPECTED(status != LXB_STATUS_OK)) { + break; + } + if (ctx.result) { + ret = current; + break; + } + current = current->parent; + } + } + + dom_selector_cleanup(&parser, &selectors, list); + + return ret; +} + +/* https://dom.spec.whatwg.org/#dom-parentnode-queryselector */ +void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str) +{ + xmlNodePtr result = NULL; + + if (dom_query_selector_common( + thisp, + intern, + selectors_str, + dom_query_selector_find_single_callback, + &result, + LXB_SELECTORS_OPT_MATCH_FIRST + ) != LXB_STATUS_OK || result == NULL) { + RETURN_NULL(); + } else { + DOM_RET_OBJ(result, intern); + } +} + +/* https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall */ +void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str) +{ + HashTable *list = zend_new_array(0); + dom_query_selector_all_ctx ctx = { list, intern }; + + if (dom_query_selector_common( + thisp, + intern, + selectors_str, + dom_query_selector_find_array_callback, + &ctx, + LXB_SELECTORS_OPT_DEFAULT + ) != LXB_STATUS_OK) { + zend_array_destroy(list); + RETURN_THROWS(); + } else { + php_dom_create_iterator(return_value, DOM_NODELIST, true); + dom_object *ret_obj = Z_DOMOBJ_P(return_value); + dom_nnodemap_object *mapptr = (dom_nnodemap_object *) ret_obj->ptr; + ZVAL_ARR(&mapptr->baseobj_zv, list); + mapptr->nodetype = DOM_NODESET; + } +} + +/* https://dom.spec.whatwg.org/#dom-element-matches */ +void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str) +{ + dom_query_selector_matches_ctx ctx = { thisp, false }; + + if (dom_query_matches( + thisp, + intern, + selectors_str, + &ctx + ) != LXB_STATUS_OK) { + RETURN_THROWS(); + } else { + RETURN_BOOL(ctx.result); + } +} + +/* https://dom.spec.whatwg.org/#dom-element-closest */ +void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str) +{ + const xmlNode *result = dom_query_closest(thisp, intern, selectors_str); + if (EXPECTED(result != NULL)) { + DOM_RET_OBJ((xmlNodePtr) result, intern); + } +} + +#endif diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode/tree.c similarity index 99% rename from ext/dom/parentnode.c rename to ext/dom/parentnode/tree.c index ce0373d4c8f77..7f2dcbc1e53b4 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode/tree.c @@ -1,6 +1,4 @@ /* - +----------------------------------------------------------------------+ - | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ @@ -23,9 +21,9 @@ #include "php.h" #if defined(HAVE_LIBXML) && defined(HAVE_DOM) -#include "php_dom.h" -#include "internal_helpers.h" -#include "dom_properties.h" +#include "../php_dom.h" +#include "../internal_helpers.h" +#include "../dom_properties.h" /* {{{ firstElementChild DomParentNode readonly=yes diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 1b214d1154467..81c7e11006724 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -131,8 +131,6 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type); zend_object *dom_xpath_objects_new(zend_class_entry *class_type); #endif bool dom_get_strict_error(php_libxml_ref_obj *document); -void php_dom_throw_error(int error_code, bool strict_error); -void php_dom_throw_error_with_message(int error_code, char *error_message, bool strict_error); void node_list_unlink(xmlNodePtr node); int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len); xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix); @@ -198,6 +196,10 @@ bool php_dom_fragment_insertion_hierarchy_check_replace(xmlNodePtr parent, xmlNo void php_dom_node_append(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent); bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point); bool php_dom_pre_insert_is_parent_invalid(xmlNodePtr parent); +void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str); +void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str); +void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str); +void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str); /* nodemap and nodelist APIs */ xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform); diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php index 196a858817572..ccf39c1c8fce0 100644 --- a/ext/dom/php_dom.stub.php +++ b/ext/dom/php_dom.stub.php @@ -1114,6 +1114,9 @@ interface ParentNode public function append(Node|string ...$nodes): void; public function prepend(Node|string ...$nodes): void; public function replaceChildren(Node|string ...$nodes): void; + + public function querySelector(string $selectors): ?Element; + public function querySelectorAll(string $selectors): NodeList; } interface ChildNode @@ -1373,6 +1376,11 @@ public function append(Node|string ...$nodes): void {} public function prepend(Node|string ...$nodes): void {} /** @implementation-alias DOMElement::replaceChildren */ public function replaceChildren(Node|string ...$nodes): void {} + + public function querySelector(string $selectors): ?Element {} + public function querySelectorAll(string $selectors): NodeList {} + public function closest(string $selectors): ?Element {} + public function matches(string $selectors): bool {} } class Attr extends Node @@ -1489,6 +1497,11 @@ public function append(Node|string ...$nodes): void {} public function prepend(Node|string ...$nodes): void {} /** @implementation-alias DOMElement::replaceChildren */ public function replaceChildren(Node|string ...$nodes): void {} + + /** @implementation-alias DOM\Element::querySelector */ + public function querySelector(string $selectors): ?Element {} + /** @implementation-alias DOM\Element::querySelectorAll */ + public function querySelectorAll(string $selectors): NodeList {} } class Entity extends Node @@ -1583,6 +1596,12 @@ public function prepend(Node|string ...$nodes): void {} public function replaceChildren(Node|string ...$nodes): void {} public function importLegacyNode(\DOMNode $node, bool $deep = false): Node {} + + /** @implementation-alias DOM\Element::querySelector */ + public function querySelector(string $selectors): ?Element {} + + /** @implementation-alias DOM\Element::querySelectorAll */ + public function querySelectorAll(string $selectors): NodeList {} } final class HTMLDocument extends Document diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h index a386d5217dc9b..a12c2a61e3186 100644 --- a/ext/dom/php_dom_arginfo.h +++ b/ext/dom/php_dom_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f441c789fdce91e8fc71f450b294c11059999af1 */ + * Stub hash: 120b3e764f4d83c93918b81e1a73601bb7fe7cf0 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0) ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0) @@ -567,6 +567,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_DOM_ParentNode_replaceChildren arginfo_class_DOM_ParentNode_append +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_ParentNode_querySelector, 0, 1, DOM\\Element, 1) + ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_ParentNode_querySelectorAll, 0, 1, DOM\\\116odeList, 0) + ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_DOM_ChildNode_remove arginfo_class_DOMChildNode_remove #define arginfo_class_DOM_ChildNode_before arginfo_class_DOM_ParentNode_append @@ -829,6 +837,16 @@ ZEND_END_ARG_INFO() #define arginfo_class_DOM_Element_replaceChildren arginfo_class_DOM_ParentNode_append +#define arginfo_class_DOM_Element_querySelector arginfo_class_DOM_ParentNode_querySelector + +#define arginfo_class_DOM_Element_querySelectorAll arginfo_class_DOM_ParentNode_querySelectorAll + +#define arginfo_class_DOM_Element_closest arginfo_class_DOM_ParentNode_querySelector + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_Element_matches, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, selectors, IS_STRING, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_DOM_Attr_isId arginfo_class_DOM_Node_hasChildNodes ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOM_CharacterData_substringData, 0, 2, IS_STRING, 0) @@ -886,6 +904,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_DOM_DocumentFragment_replaceChildren arginfo_class_DOM_ParentNode_append +#define arginfo_class_DOM_DocumentFragment_querySelector arginfo_class_DOM_ParentNode_querySelector + +#define arginfo_class_DOM_DocumentFragment_querySelectorAll arginfo_class_DOM_ParentNode_querySelectorAll + #define arginfo_class_DOM_Document_getElementsByTagName arginfo_class_DOM_Element_getElementsByTagName #define arginfo_class_DOM_Document_getElementsByTagNameNS arginfo_class_DOM_Element_getElementsByTagNameNS @@ -981,6 +1003,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_Document_importLegacyNo ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, deep, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() +#define arginfo_class_DOM_Document_querySelector arginfo_class_DOM_ParentNode_querySelector + +#define arginfo_class_DOM_Document_querySelectorAll arginfo_class_DOM_ParentNode_querySelectorAll + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOM_HTMLDocument_createEmpty, 0, 0, DOM\\HTMLDocument, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 0, "\"UTF-8\"") ZEND_END_ARG_INFO() @@ -1263,6 +1289,10 @@ ZEND_METHOD(DOM_Element, getElementsByTagName); ZEND_METHOD(DOM_Element, getElementsByTagNameNS); ZEND_METHOD(DOM_Element, insertAdjacentElement); ZEND_METHOD(DOM_Element, setIdAttributeNode); +ZEND_METHOD(DOM_Element, querySelector); +ZEND_METHOD(DOM_Element, querySelectorAll); +ZEND_METHOD(DOM_Element, closest); +ZEND_METHOD(DOM_Element, matches); ZEND_METHOD(DOM_CharacterData, appendData); ZEND_METHOD(DOM_CharacterData, insertData); ZEND_METHOD(DOM_CharacterData, deleteData); @@ -1549,6 +1579,8 @@ static const zend_function_entry class_DOM_ParentNode_methods[] = { ZEND_RAW_FENTRY("append", NULL, arginfo_class_DOM_ParentNode_append, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) ZEND_RAW_FENTRY("prepend", NULL, arginfo_class_DOM_ParentNode_prepend, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) ZEND_RAW_FENTRY("replaceChildren", NULL, arginfo_class_DOM_ParentNode_replaceChildren, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("querySelector", NULL, arginfo_class_DOM_ParentNode_querySelector, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) + ZEND_RAW_FENTRY("querySelectorAll", NULL, arginfo_class_DOM_ParentNode_querySelectorAll, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT, NULL, NULL) ZEND_FE_END }; @@ -1656,6 +1688,10 @@ static const zend_function_entry class_DOM_Element_methods[] = { ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_Element_append, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_Element_prepend, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_DOM_Element_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(DOM_Element, querySelector, arginfo_class_DOM_Element_querySelector, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, querySelectorAll, arginfo_class_DOM_Element_querySelectorAll, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, closest, arginfo_class_DOM_Element_closest, ZEND_ACC_PUBLIC) + ZEND_ME(DOM_Element, matches, arginfo_class_DOM_Element_matches, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1707,6 +1743,8 @@ static const zend_function_entry class_DOM_DocumentFragment_methods[] = { ZEND_RAW_FENTRY("append", zim_DOMElement_append, arginfo_class_DOM_DocumentFragment_append, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_DocumentFragment_prepend, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("replaceChildren", zim_DOMElement_replaceChildren, arginfo_class_DOM_DocumentFragment_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("querySelector", zim_DOM_Element_querySelector, arginfo_class_DOM_DocumentFragment_querySelector, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("querySelectorAll", zim_DOM_Element_querySelectorAll, arginfo_class_DOM_DocumentFragment_querySelectorAll, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_FE_END }; @@ -1754,6 +1792,8 @@ static const zend_function_entry class_DOM_Document_methods[] = { ZEND_RAW_FENTRY("prepend", zim_DOMElement_prepend, arginfo_class_DOM_Document_prepend, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("replaceChildren", zim_DOMDocument_replaceChildren, arginfo_class_DOM_Document_replaceChildren, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(DOM_Document, importLegacyNode, arginfo_class_DOM_Document_importLegacyNode, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("querySelector", zim_DOM_Element_querySelector, arginfo_class_DOM_Document_querySelector, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_RAW_FENTRY("querySelectorAll", zim_DOM_Element_querySelectorAll, arginfo_class_DOM_Document_querySelectorAll, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_FE_END }; diff --git a/ext/dom/tests/modern/css_selectors/attribute.phpt b/ext/dom/tests/modern/css_selectors/attribute.phpt new file mode 100644 index 0000000000000..05ec94ee02b48 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/attribute.phpt @@ -0,0 +1,118 @@ +--TEST-- +CSS Selectors - Attribute +--EXTENSIONS-- +dom +--FILE-- + + + + + + +XML); + +echo "=== Case sensitive ===\n"; + +test_helper($dom, 'a[title]'); +test_helper($dom, 'a[title="http://example.com"]'); +test_helper($dom, 'a[title="http://example."]'); +test_helper($dom, 'a[title*="example"]'); +test_helper($dom, 'a[title*=""]'); +test_helper($dom, 'a[title^="HTTP"]'); +test_helper($dom, 'a[title^="http"]'); +test_helper($dom, 'a[title^="http"][title$=".be"]'); +test_helper($dom, 'a[title$=".com"]'); +test_helper($dom, 'a[title$=".foo"]'); +test_helper($dom, 'a[lang|="nl"]'); +test_helper($dom, 'a[lang|="nl-be"]'); +test_helper($dom, 'a[tokens~="def"]'); +test_helper($dom, 'a[tokens~="de"]'); +test_helper($dom, 'a[tokens~="def ghi"]'); + +echo "=== Case insensitive ===\n"; + +test_helper($dom, 'a[title]'); +test_helper($dom, 'a[title="http://example.COM" i]'); +test_helper($dom, 'a[title="http://EXAMPLE." i]'); +test_helper($dom, 'a[title*="ExAmPlE" i]'); +test_helper($dom, 'a[title^="HTTP" i]'); +test_helper($dom, 'a[title^="HTTP" i][title$=".be"]'); +test_helper($dom, 'a[title$=".COM" i]'); +test_helper($dom, 'a[lang|="NL" i]'); +test_helper($dom, 'a[lang|="NL-BE" i]'); +test_helper($dom, 'a[tokens~="DE" i]'); +test_helper($dom, 'a[tokens~="DEF" i]'); +test_helper($dom, 'a[tokens~="DEF GHI" i]'); + +?> +--EXPECT-- +=== Case sensitive === +--- Selector: a[title] --- + + + + +--- Selector: a[title="http://example.com"] --- + +--- Selector: a[title="http://example."] --- +--- Selector: a[title*="example"] --- + + + + +--- Selector: a[title*=""] --- +--- Selector: a[title^="HTTP"] --- +--- Selector: a[title^="http"] --- + + +--- Selector: a[title^="http"][title$=".be"] --- + +--- Selector: a[title$=".com"] --- + +--- Selector: a[title$=".foo"] --- +--- Selector: a[lang|="nl"] --- + + +--- Selector: a[lang|="nl-be"] --- + + +--- Selector: a[tokens~="def"] --- + +--- Selector: a[tokens~="de"] --- +--- Selector: a[tokens~="def ghi"] --- +=== Case insensitive === +--- Selector: a[title] --- + + + + +--- Selector: a[title="http://example.COM" i] --- + +--- Selector: a[title="http://EXAMPLE." i] --- +--- Selector: a[title*="ExAmPlE" i] --- + + + + +--- Selector: a[title^="HTTP" i] --- + + +--- Selector: a[title^="HTTP" i][title$=".be"] --- + +--- Selector: a[title$=".COM" i] --- + +--- Selector: a[lang|="NL" i] --- + + +--- Selector: a[lang|="NL-BE" i] --- + + +--- Selector: a[tokens~="DE" i] --- +--- Selector: a[tokens~="DEF" i] --- + +--- Selector: a[tokens~="DEF GHI" i] --- diff --git a/ext/dom/tests/modern/css_selectors/closest.phpt b/ext/dom/tests/modern/css_selectors/closest.phpt new file mode 100644 index 0000000000000..363ebe9f2fca1 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/closest.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test DOM\Element::closest() method: legit cases +--EXTENSIONS-- +dom +--FILE-- + + +
+
+
+
+
+ +XML; + +$dom = DOM\XMLDocument::createFromString($xml); + +function test($el, $selector) { + echo "--- Selector: $selector ---\n"; + var_dump($el->closest($selector)?->getAttribute('xml:id')); +} + +test($dom->getElementById('div3'), 'div'); +test($dom->getElementById('div3'), '[class="foo"]'); +test($dom->getElementById('div3'), ':not(root)'); +test($dom->getElementById('div3'), ':not(div)'); +test($dom->getElementById('div3'), 'a'); +test($dom->getElementById('div3'), 'root :not(div[class])'); +test($dom->getElementById('div3'), 'root > :not(div[class])'); + +?> +--EXPECT-- +--- Selector: div --- +string(4) "div3" +--- Selector: [class="foo"] --- +string(4) "div1" +--- Selector: :not(root) --- +string(4) "div3" +--- Selector: :not(div) --- +NULL +--- Selector: a --- +NULL +--- Selector: root :not(div[class]) --- +string(4) "div2" +--- Selector: root > :not(div[class]) --- +NULL diff --git a/ext/dom/tests/modern/css_selectors/closest_invalid_selector.phpt b/ext/dom/tests/modern/css_selectors/closest_invalid_selector.phpt new file mode 100644 index 0000000000000..81db6a72b6867 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/closest_invalid_selector.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test DOM\Element::closest() method: invalid selector +--EXTENSIONS-- +dom +--FILE-- +"); + +try { + var_dump($dom->documentElement->closest('@invalid')); +} catch (DOMException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- +Invalid selector (Selectors. Unexpected token: @invalid) diff --git a/ext/dom/tests/modern/css_selectors/combinators.phpt b/ext/dom/tests/modern/css_selectors/combinators.phpt new file mode 100644 index 0000000000000..104c2408cc3b5 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/combinators.phpt @@ -0,0 +1,113 @@ +--TEST-- +CSS Selectors - Combinators +--EXTENSIONS-- +dom +--FILE-- + + + +

First p

+

Second p

+ +
+

Third p

+ + +
+

Fourth p

+
+
+
+

Fifth p

+
+ + + + + + + + + + + +
ABC
+ + +HTML); + +test_helper($dom, 'nonsense'); +test_helper($dom, 'p'); +test_helper($dom, 'p, img'); +test_helper($dom, 'body p'); +test_helper($dom, 'body div p'); +test_helper($dom, 'div > *'); +test_helper($dom, 'div > p'); +test_helper($dom, 'div > p + img'); +test_helper($dom, 'div > p ~ img'); +test_helper($dom, 'body > img'); +test_helper($dom, 'div.bar.baz > p'); +test_helper($dom, 'article[title].bar p'); + +try { + test_helper($dom, 'col.selected||td'); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +--- Selector: nonsense --- +--- Selector: p --- +

First p

+

Second p

+

Third p

+

Fourth p

+

Fifth p

+--- Selector: p, img --- +

First p

+

Second p

+ +

Third p

+ + +

Fourth p

+

Fifth p

+--- Selector: body p --- +

First p

+

Second p

+

Third p

+

Fourth p

+

Fifth p

+--- Selector: body div p --- +

Third p

+

Fourth p

+--- Selector: div > * --- +

Third p

+ + +
+

Fourth p

+
+

Fourth p

+--- Selector: div > p --- +

Third p

+

Fourth p

+--- Selector: div > p + img --- + +--- Selector: div > p ~ img --- + + +--- Selector: body > img --- + +--- Selector: div.bar.baz > p --- +

Fourth p

+--- Selector: article[title].bar p --- +

Fifth p

+--- Selector: col.selected||td --- +DOM\Document::querySelectorAll(): Argument #1 ($selectors) contains an unsupported selector diff --git a/ext/dom/tests/modern/css_selectors/id.phpt b/ext/dom/tests/modern/css_selectors/id.phpt new file mode 100644 index 0000000000000..21af9b1f1a680 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/id.phpt @@ -0,0 +1,35 @@ +--TEST-- +CSS Selectors - ID +--EXTENSIONS-- +dom +--FILE-- + + + + + + + +XML); + +test_helper($dom, '#test'); +test_helper($dom, '#test1'); +test_helper($dom, '#test2'); +test_helper($dom, '#test3'); +test_helper($dom, '#test4'); + +?> +--EXPECT-- +--- Selector: #test --- +--- Selector: #test1 --- + +--- Selector: #test2 --- + +--- Selector: #test3 --- + +--- Selector: #test4 --- diff --git a/ext/dom/tests/modern/css_selectors/matches_invalid_selector.phpt b/ext/dom/tests/modern/css_selectors/matches_invalid_selector.phpt new file mode 100644 index 0000000000000..11cc4a942d247 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/matches_invalid_selector.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test DOM\Element::matches() method: invalid selector +--EXTENSIONS-- +dom +--FILE-- +"); + +try { + var_dump($dom->documentElement->matches('@invalid')); +} catch (DOMException $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- +Invalid selector (Selectors. Unexpected token: @invalid) diff --git a/ext/dom/tests/modern/css_selectors/namespaces.phpt b/ext/dom/tests/modern/css_selectors/namespaces.phpt new file mode 100644 index 0000000000000..df4782d1191f4 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/namespaces.phpt @@ -0,0 +1,54 @@ +--TEST-- +CSS Selectors - Namespaces +--EXTENSIONS-- +dom +--FILE-- + + + +
+ + + + + + +XML); + +$container = $dom->documentElement->firstElementChild; +$container->setAttribute("foo:bar", "baz"); +$container->setAttributeNS("urn:a", "a:bar", "baz"); + +test_helper($dom, 'container[align]'); +test_helper($dom, 'container[foo\\:bar]'); +test_helper($dom, 'container[a\\:bar]'); +test_helper($dom, 'container[bar]'); + +test_helper($dom, 'a:first-of-type'); +test_helper($dom, 'a:last-of-type'); + +test_failure($dom, 'container[* | bar]'); + +?> +--EXPECT-- +--- Selector: container[align] --- + +--- Selector: container[foo\:bar] --- + +--- Selector: container[a\:bar] --- +--- Selector: container[bar] --- +--- Selector: a:first-of-type --- + + + +--- Selector: a:last-of-type --- + + + +--- Selector: container[* | bar] --- +Code 12 Invalid selector (Selectors. Unexpected token: *) diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_blank.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_blank.phpt new file mode 100644 index 0000000000000..b1be7db685d4d --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_blank.phpt @@ -0,0 +1,23 @@ +--TEST-- +CSS Selectors - Pseudo classes: blank +--EXTENSIONS-- +dom +--FILE-- + +XML); + +try { + test_helper($dom, ':blank'); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +--- Selector: :blank --- +:blank selector is not implemented because CSSWG has not yet decided its semantics (https://github.com/w3c/csswg-drafts/issues/1967) diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_checked.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_checked.phpt new file mode 100644 index 0000000000000..6341386c49c75 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_checked.phpt @@ -0,0 +1,27 @@ +--TEST-- +CSS Selectors - Pseudo classes: checked +--EXTENSIONS-- +dom +--FILE-- + + + + diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_current.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_current.phpt new file mode 100644 index 0000000000000..101a3d397ed26 --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_current.phpt @@ -0,0 +1,21 @@ +--TEST-- +CSS Selectors - Pseudo classes: current +--EXTENSIONS-- +dom +--FILE-- + +
+ +XML); + +test_helper($dom, ':current(div)'); + +?> +--EXPECT-- +--- Selector: :current(div) --- +
diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_dir.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_dir.phpt new file mode 100644 index 0000000000000..1f82f5046974b --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_dir.phpt @@ -0,0 +1,27 @@ +--TEST-- +CSS Selectors - Pseudo classes: dir +--EXTENSIONS-- +dom +--FILE-- + +

1

+ +

2

+ + +XML); + +test_failure($dom, ':dir(rtl)', true); +test_failure($dom, ':dir(ltr)', true); + +?> +--EXPECT-- +--- Selector: :dir(rtl) --- +Code 12 Invalid selector (Selectors. Not supported: dir) +--- Selector: :dir(ltr) --- +Code 12 Invalid selector (Selectors. Not supported: dir) diff --git a/ext/dom/tests/modern/css_selectors/pseudo_classes_disabled_enabled.phpt b/ext/dom/tests/modern/css_selectors/pseudo_classes_disabled_enabled.phpt new file mode 100644 index 0000000000000..03b88456d127c --- /dev/null +++ b/ext/dom/tests/modern/css_selectors/pseudo_classes_disabled_enabled.phpt @@ -0,0 +1,107 @@ +--TEST-- +CSS Selectors - Pseudo classes: disabled/enabled +--EXTENSIONS-- +dom +--FILE-- + + + + + + + + + +
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ + +