/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2022 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Stream parser for HTML 5 * https://w3c.github.io/html-reference/syntax.html * */ #include #define FAIL_CHAR 0x08 static uint8_t css_lextable[] = { /* the css property names */ #include "css-lextable.h" }; static uint8_t css_propconst_lextable[] = { /* the css property values */ #include "css-propconst-lextable.h" }; #define LHP_AC_GRANULE 512 enum { /* html */ LHPS_INIT, /* default css injection */ LHPS_OUTER, LHPS_TAG, LHPS_BAD_TAG, LHPS_DO_START_ELEM, LHPS_ATTRIB, LHPS_ATTRIB_VAL, LHPS_AMP, LHPS_AMPHASH, LHPS_AMPHASH_HEX, LHPS_SCOMMENT1, LHPS_SCOMMENT2, LHPS_COMMENT, LHPS_ECOMMENT1, LHPS_ECOMMENT2, /* css */ LCSPS_CSS_OUTER, LCSPS_CCOM_S1, LCSPS_CCOM_E1, LCSPS_CCOM, LCSPS_CSS_OUTER_TAG1, LCSPS_CSS_NAMES, LCSPS_CSS_DEF_NAME, LCSPS_CSS_DEF_VALUE, LCSPS_SCOMMENT1, LCSPS_SCOMMENT2, LCSPS_COMMENT, LCSPS_ECOMMENT1, LCSPS_ECOMMENT2, LCSPS_CSS_STANZA, }; /* * 17 well-known colours specified by CSS 2.1 * https://www.w3.org/TR/CSS21/syndata.html#value-def-color */ #if 0 static struct cols { const char * const name; uint32_t rgba; } cols[] = { { "maroon", LWSDC_RGBA(0x80, 0x00, 0x00, 255) }, { "red", LWSDC_RGBA(0xff, 0x00, 0x00, 255) }, { "orange", LWSDC_RGBA(0xff, 0xa5, 0x00, 255) }, { "yellow", LWSDC_RGBA(0xff, 0xff, 0x00, 255) }, { "olive", LWSDC_RGBA(0x80, 0x80, 0x00, 255) }, { "purple", LWSDC_RGBA(0x80, 0x00, 0x80, 255) }, { "fuchsia", LWSDC_RGBA(0xff, 0x00, 0xff, 255) }, { "white", LWSDC_RGBA(0xff, 0xff, 0xff, 255) }, { "lime", LWSDC_RGBA(0x00, 0xff, 0x00, 255) }, { "green", LWSDC_RGBA(0x00, 0x80, 0x00, 255) }, { "navy", LWSDC_RGBA(0x00, 0x00, 0x80, 255) }, { "blue", LWSDC_RGBA(0x00, 0x00, 0xff, 255) }, { "aqua", LWSDC_RGBA(0x00, 0xff, 0xff, 255) }, { "teal", LWSDC_RGBA(0x00, 0x80, 0x80, 255) }, { "black", LWSDC_RGBA(0x00, 0x00, 0x00, 255) }, { "silver", LWSDC_RGBA(0xc0, 0xc0, 0xc0, 255) }, { "gray", LWSDC_RGBA(0x80, 0x80, 0x80, 255) }, }; #endif /* * "void elements" are html elements that don't have a scope, and so don't * have a scope closure */ static const char * const void_elems[] = { "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" }; static const uint8_t void_elems_lens[] = /* lengths for the table above */ { 4, 4, 2, 3, 7, 5, 2, 3, 5, 6, 4, 4, 5, 6, 5, 3 }; static const char *const default_css = "/* lws_lhp default css */" "html, address,blockquote, dd, div,dl, dt, fieldset, form, frame, " "frameset, h1, h2, h3, h4, h5, h6, noframes, ol, p, ul, center, " "dir, hr, menu, pre { top: 0px; right: 0px; bottom: 0px; left: 0px;" " unicode-bidi: embed; color: #000;" "padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px;" "margin-top: 2px; margin-left: 2px; margin-bottom: 2px; margin-right: 2px;" "position: static; width: auto; height: auto;" "}\n" "div { display: block; width: auto; }\n" "body { display: block}\n" "li { display: list-item }\n" "head { display: none }\n" "table { display: table; }\n" "tr { display: table-row }\n" "thead { display: table-header-group }\n" "tbody { display: table-row-group }\n" "tfoot { display: table-footer-group }\n" "col { display: table-column }\n" "colgroup { display: table-column-group }\n" "td, th { display: table-cell }\n" "caption { display: table-caption }\n" "th { font-weight: bolder; text-align: center }\n" "caption { text-align: center }\n" "body { margin: 8px }\n" "h1 { font-size: 2em; margin: .67em 0 }\n" "h2 { font-size: 1.5em; margin: .75em 0 }\n" "h3 { font-size: 1.17em; margin: .83em 0 }\n" "h4, p, blockquote, ul, fieldset, form, ol, dl, dir, menu " "{ margin: 1.12em 0 }\n" "h5 { font-size: .83em; margin: 1.5em 0 }\n" "h6 { font-size: .75em; margin: 1.67em 0 }\n" "h1, h2, h3, h4, h5, h6, b, strong { font-weight: bolder }\n" "blockquote { margin-left: 40px; margin-right: 40px }\n" "i, cite, em, var, address { font-style: italic }\n" " pre, tt, code, kbd, samp { font-family: monospace }\n" "pre { white-space: pre }\n" "button, textarea, input, select { display: inline-block }\n" "big { font-size: 1.17em }\n" "small, sub, sup { font-size: .83em }\n" "sub { vertical-align: sub }\n" "sup { vertical-align: super }\n" "table { border-spacing: 2px; padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px; margin-top: 2px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px }\n" "thead, tbody, tfoot { vertical-align: middle }\n" "td, th, tr { vertical-align: inherit; width: auto; padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px; margin-top: 2px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px }\n" "s, strike, del { text-decoration: line-through }\n" "hr { border: 1px inset }\n" "ol, ul, dir, menu, dd { margin-left: 40px }\n" "ol { list-style-type: decimal }\n" "ol ul, ul ol, ul ul, ol ol { margin-top: 0; margin-bottom: 0 }\n" "u, ins { text-decoration: underline }\n" "br:before { content: \"A\"; white-space: pre-line }\n" "center { text-align: center }\n" ":link, :visited { text-decoration: underline }\n" ":focus { outline: thin dotted invert }\n" "BDO[DIR=\"ltr\"] { direction: ltr; unicode-bidi: bidi-override }" "BDO[DIR=\"rtl\"] { direction: rtl; unicode-bidi: bidi-override }" "*[DIR=\"ltr\"] { direction: ltr; unicode-bidi: embed }" "*[DIR=\"rtl\"] { direction: rtl; unicode-bidi: embed }" "@media print {" " h1 { page-break-before: always }\n" " h1, h2, h3, h4, h5, h6 { page-break-after: avoid }\n" " ul, ol, dl { page-break-before: avoid }\n" "}\n" ; static int lhp_clean_atr(lws_dll2_t *d, void *user) { lhp_atr_t *atr = lws_container_of(d, lhp_atr_t, list); lws_dll2_remove(d); lws_free(atr); return 0; } static void lhp_clean_level(lhp_pstack_t *ps) { lws_dll2_foreach_safe(&ps->atr, NULL, lhp_clean_atr); lws_dll2_remove(&ps->list); lws_free(ps); } int lws_lhp_construct(lhp_ctx_t *ctx, lhp_callback cb, void *user, const lws_surface_info_t *ic) { lhp_pstack_t *ps = lws_zalloc(sizeof(*ps), __func__); if (!ps) return 1; memset(ctx, 0, sizeof(*ctx) - sizeof(ctx->buf)); ctx->user = user; ctx->ic = *ic; /* * these are done implicitly by the memset above * ctx->state = LHPS_INIT; * ctx->sp = 0; */ ps->cb = cb; lws_dll2_add_tail(&ps->list, &ctx->stack); return 0; } static int lhp_clean_stack(lws_dll2_t *d, void *user) { lhp_pstack_t *ps = lws_container_of(d, lhp_pstack_t, list); lhp_clean_level(ps); return 0; } static const lws_fx_t c_254= { 2,54000000 }, c_10 = { 10,0 }, c_72 = { 72,0 }, c_6 = { 6,0 }, c_100 = { 100,0 }; /* * We need to go backward until we reach an absolute length for the reference * axis, then base off that and go forward applying relative operations (like %) * on it in order. */ static int lws_css_compute_cascaded_length(lhp_ctx_t *ctx, int ref, lhp_pstack_t *ps, lws_fx_t *t1) { lhp_pstack_t *psb = ps, *psmap[20]; const struct lcsp_atr *atrmap[20]; lws_fx_t t2; int amp = 0; do { const struct lcsp_atr *a; psb = lws_css_get_parent_block(ctx, psb); if (!psb) break; a = (ref == LWS_LHPREF_WIDTH) ? psb->css_width : psb->css_height; if (!a) /* skip levels that don't change it */ continue; if (amp + 1 == LWS_ARRAY_SIZE(atrmap)) /* uhh... */ break; psmap[amp] = psb; atrmap[amp++] = a; if (a->unit == LCSP_UNIT_LENGTH_PERCENT || a->unit == LCSP_UNIT_ANGLE_REL_DEG || a->unit == LCSP_UNIT_NONE) /* need earlier info to compute... keep going back */ continue; break; } while (1); /* * We have the path back through the elements to the first * absolute one */ while (amp-- > 0) { if (atrmap[amp]->unit != LCSP_UNIT_LENGTH_PERCENT) { *t1 = *lws_csp_px(atrmap[amp], psmap[amp]); } else if (amp) lws_fx_div(t1, lws_fx_mul(&t2, &atrmap[amp]->u.i, t1), &c_100); } return 0; } const lws_fx_t * lws_csp_px(const lcsp_atr_t *a, lhp_pstack_t *ps) { lhp_ctx_t *ctx; const lws_display_font_t *f; lws_fx_t t1, t2, t3; int ref; assert(ps); if (!a) return NULL; ctx = lws_container_of(ps->list.owner, lhp_ctx_t, stack); f = ps->font; ref = lhp_prop_axis(a); switch (a->unit) { case LCSP_UNIT_LENGTH_EM: return lws_fx_mul((lws_fx_t *)&a->r, &a->u.i, &f->em); case LCSP_UNIT_LENGTH_EX: return lws_fx_mul((lws_fx_t *)&a->r, &a->u.i, &f->ex); case LCSP_UNIT_LENGTH_IN: /* (inches * 2.54 * hwmm) / hwpx */ if (ref == LWS_LHPREF_NONE) break; return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, lws_fx_mul(&t3, &a->u.i, &c_254), &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); case LCSP_UNIT_LENGTH_CM: /* (cm * 10 * hwmm) / hwpx */ if (ref == LWS_LHPREF_NONE) break; return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, lws_fx_mul(&t3, &a->u.i, &c_10), &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); case LCSP_UNIT_LENGTH_MM: /* (mm * hwmm) / hwpx */ if (ref == LWS_LHPREF_NONE) break; return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, &a->u.i, &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); case LCSP_UNIT_LENGTH_PT: /* ((pt * 2.54 * hwmm) / hwpx ) / 72 */ if (ref == LWS_LHPREF_NONE) break; return lws_fx_div((lws_fx_t *)&a->r, lws_fx_div(&t1, lws_fx_mul(&t2, lws_fx_mul(&t3, &a->u.i, &c_254), &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]), &c_72); case LCSP_UNIT_LENGTH_PC: /* ((pc * 2.54 * hwmm) / hwpx ) / 6 */ if (ref == LWS_LHPREF_NONE) break; return lws_fx_div((lws_fx_t *)&a->r, lws_fx_div(&t1, lws_fx_mul(&t2, lws_fx_mul(&t3, &a->u.i, &c_254), &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]), &c_6); case LCSP_UNIT_LENGTH_PX: /* px */ return &a->u.i; case LCSP_UNIT_LENGTH_PERCENT: /* (percent * psb->w) / 100 */ if (ref == LWS_LHPREF_NONE) break; t1.whole = 0; t1.frac = 0; lws_css_compute_cascaded_length(ctx, ref, ps, &t1); return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, &a->u.i, &t1), &c_100); default: break; } return &a->u.i; } static lhp_atr_t * lhp_atr_new(lhp_ctx_t *ctx, size_t name_len, size_t value_len) { lhp_pstack_t *ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list); /* create the element name attribute */ lhp_atr_t *a = lws_malloc(sizeof(*a) + name_len + 1 + value_len + 1, "html_elem_atr"); size_t n; if (!a) return NULL; if (!ps->atr.count) { /* only check the tag string, not the attributes */ ctx->u.f.void_element = 0; /* * mark ps that are elements that contain others for layout as * being the parent block */ if ((name_len == 4 && !strncmp(ctx->buf, "body", 4)) || (name_len == 3 && !strncmp(ctx->buf, "div", 3))) ps->is_block = 1; for (n = 0; n < LWS_ARRAY_SIZE(void_elems); n++) if (ctx->npos == void_elems_lens[n] && !strncmp(void_elems[n], ctx->buf, (size_t)ctx->npos)) ctx->u.f.void_element = 1; } lws_dll2_clear(&a->list); a->name_len = name_len; a->value_len = value_len; ctx->buf[ctx->npos] = '\0'; memcpy(&a[1], ctx->buf, (unsigned int)ctx->npos + 1u); *(((uint8_t *)&a[1]) + name_len) = '\0'; lws_dll2_add_tail(&a->list, &ps->atr); ctx->npos = 0; return a; } static int hspace(uint8_t c) { return c == ' ' || c == 9 || c == 10 || c == 12 || c == 13; } void lhp_uni_emit(lhp_ctx_t *ctx) { /* emit */ if (ctx->temp <= 0x7f) { ctx->buf[ctx->npos++] = (char)(ctx->temp & 0x7f); return; } if (ctx->temp <= 0x7ff) { ctx->buf[ctx->npos++] = (char)(0xc0 | ((uint8_t)(ctx->temp >> 6) & 0x1f)); goto a; } if (ctx->temp <= 0xffff) { ctx->buf[ctx->npos++] = (char)(0xe0 | ((uint8_t)(ctx->temp >> 12) & 0xf)); goto b; } if (ctx->temp <= 0x10ffff) { ctx->buf[ctx->npos++] = (char)(0xf0 | ((uint8_t)(ctx->temp >> 18) & 7)); ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp >> 12) & 0x3f)); } b: ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp >> 6) & 0x3f)); a: ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp) & 0x3f)); } static int lcsp_append_cssval_int(lhp_ctx_t *ctx) { lcsp_atr_t *atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr), LHP_AC_GRANULE); if (!atr) return 1; /* add this prop value atr to the def */ //lwsl_err("%s: tf %d.%u\n", __func__, ctx->tf.whole, ctx->tf.frac); atr->u.i = ctx->tf; atr->unit = ctx->unit; lws_dll2_add_tail(&atr->list, &ctx->def->atrs); return 0; } static int lcsp_append_cssval_color(lhp_ctx_t *ctx) { lcsp_atr_t *atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr), LHP_AC_GRANULE); unsigned int r, g, b, a = 0xff; if (!atr) return 1; /* add this prop value atr to the def */ switch (ctx->temp_count) { case 3: r = (ctx->temp >> 8) & 0xf; g = (ctx->temp >> 4) & 0xf; b = ctx->temp & 0xf; atr->u.rgba = (a << 24) | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r; break; case 4: r = (ctx->temp >> 12) & 0xf; g = (ctx->temp >> 8) & 0xf; b = (ctx->temp >> 4) & 0xf; a = ctx->temp & 0xf; atr->u.rgba = (a << 28) | (a << 24) | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r; break; case 6: r = (ctx->temp >> 16) & 0xff; g = (ctx->temp >> 8) & 0xff; b = (ctx->temp) & 0xff; atr->u.rgba = (a << 24) | (b << 16) | (g << 8) | r; break; case 8: r = (ctx->temp >> 24) & 0xff; g = (ctx->temp >> 16) & 0xff; b = (ctx->temp >> 8) & 0xff; a = (ctx->temp) & 0xff; atr->u.rgba = (a << 24) | (b << 16) | (g << 8) | r; break; } // lwsl_err("%s: %d, 0x%08x, 0x%08x\n", __func__, ctx->temp_count, ctx->temp, atr->u.rgba); atr->unit = LCSP_UNIT_RGBA; lws_dll2_add_tail(&atr->list, &ctx->def->atrs); ctx->u.f.color = 0; ctx->temp = 0; ctx->temp_count = 0; return 0; } static int lcsp_append_cssval_string(lhp_ctx_t *ctx) { lcsp_atr_t *atr; char *v, *c = &ctx->buf[0]; if (c[0] == '\"' || c[0] == '\'') { c++; ctx->npos--; } if (ctx->npos && (c[ctx->npos - 1] == '\"' || c[ctx->npos - 1] == '\'')) ctx->npos--; atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr) + (size_t)ctx->npos + 1u, LHP_AC_GRANULE); if (!atr) return 1; v = (char *)&atr[1]; atr->value_len = (size_t)ctx->npos; memcpy(v, c, (size_t)ctx->npos); v[ctx->npos] = '\0'; //lwsl_notice("%s: %s\n", __func__, v); lws_dll2_add_tail(&atr->list, &ctx->def->atrs); return 0; } static int lws_css_cascade_atr_match(lhp_ctx_t *ctx, const char *tag, size_t tag_len) { lws_start_foreach_dll(struct lws_dll2 *, q, ctx->css.head) { lcsp_stanza_t *stz = lws_container_of(q, lcsp_stanza_t, list); /* ... does this stanza mention our name? */ lws_start_foreach_dll(struct lws_dll2 *, z, stz->names.head) { lcsp_names_t *nm = lws_container_of(z, lcsp_names_t, list); const char *p = (const char *)&nm[1]; size_t nl = nm->name_len; if (nl && *p == '.') { /* match .mycss as mycss */ p++; nl--; } if (nl == tag_len && !memcmp(p, tag, tag_len)) { lcsp_stanza_ptr_t *sp = lwsac_use_zero( &ctx->cascadeac, sizeof(*sp), LHP_AC_GRANULE); if (!sp) return 1; sp->stz = stz; lws_dll2_add_tail(&sp->list, &ctx->active_stanzas); break; } } lws_end_foreach_dll(z); } lws_end_foreach_dll(q); return 0; } const char * lws_html_get_atr(lhp_pstack_t *ps, const char *aname, size_t aname_len) { /* look for src= attribute */ lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(&ps->atr)) { const lhp_atr_t *at = lws_container_of(p, lhp_atr_t, list); const char *ats = (const char *)&at[1]; if (at->name_len == aname_len && !strcmp(ats, aname)) return ats + aname_len + 1; } lws_end_foreach_dll(p); return NULL; } /* * Produce an ordered list of css stanzas that apply to the current html * parsing context, accounting for class="xxx" at each level */ static int lws_css_cascade(lhp_ctx_t *ctx) { lws_dll2_owner_clear(&ctx->active_stanzas); lwsac_free(&ctx->cascadeac); lws_dll2_owner_clear(&ctx->active_atr); lwsac_free(&ctx->propatrac); ctx->in_body = 0; /* let's proceed through the html element stack that applies */ lws_start_foreach_dll(struct lws_dll2 *, p, ctx->stack.head) { lhp_pstack_t *ps = lws_container_of(p, lhp_pstack_t, list); /* * if there is a css definition for the html entity at this * stack level, add its stanza to the results */ lws_start_foreach_dll(struct lws_dll2 *, ha, ps->atr.head) { lhp_atr_t *a = lws_container_of(ha, lhp_atr_t, list); struct lws_tokenize ts; memset(&ts, 0, sizeof(ts)); if (ha == ps->atr.head) { ts.start = (const char *)&a[1]; ts.len = a->name_len; } if (a->name_len == 5 && !strcmp((const char *)&a[1], "class")) { ts.start = ((const char *)&a[1]) + 5 + 1; ts.len = a->value_len; } do { ts.e = (int8_t)lws_tokenize(&ts); if (ts.e == LWS_TOKZE_TOKEN) { if (ha == ps->atr.head && ts.token_len == 4 && !memcmp(ts.token, "body", 4)) ctx->in_body = 1; /* * let's look through the css stanzas * for a tag match */ if (lws_css_cascade_atr_match(ctx, ts.token, ts.token_len)) return 1; } } while (ts.e > 0); } lws_end_foreach_dll(ha); /* * ... fill layout-related CSS lookups into the element * stack item... these are all pointers to the attribute * not necessarily computed scalars. Eg lws_csp_px() can be * used later to resolve atr like 50% to pixel values. */ ps->css_position = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_POSITION); ps->css_width = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_WIDTH); ps->css_height = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_HEIGHT); ps->css_display = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_DISPLAY); ps->css_border_radius[0] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_TOP_LEFT_RADIUS); ps->css_border_radius[1] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_TOP_RIGHT_RADIUS); ps->css_border_radius[2] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_BOTTOM_LEFT_RADIUS); ps->css_border_radius[3] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_BOTTOM_RIGHT_RADIUS); ps->css_background_color = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BACKGROUND_COLOR); ps->css_color = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_COLOR); ps->css_pos[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TOP); ps->css_pos[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_RIGHT); ps->css_pos[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BOTTOM); ps->css_pos[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_LEFT); ps->css_margin[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_TOP); ps->css_margin[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_RIGHT); ps->css_margin[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_BOTTOM); ps->css_margin[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_LEFT); ps->css_padding[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_TOP); ps->css_padding[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_RIGHT); ps->css_padding[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_BOTTOM); ps->css_padding[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_LEFT); } lws_end_foreach_dll(p); return 0; } void lws_lhp_destruct(lhp_ctx_t *ctx) { if (ctx->base_url) { free((void *)ctx->base_url); ctx->base_url = NULL; } lws_dll2_foreach_safe(&ctx->stack, NULL, lhp_clean_stack); lws_dll2_owner_clear(&ctx->active_stanzas); lws_dll2_owner_clear(&ctx->active_atr); lwsac_free(&ctx->propatrac); lwsac_free(&ctx->cascadeac); lwsac_free(&ctx->cssac); } void lws_lhp_tag_dlo_id(lhp_ctx_t *ctx, lhp_pstack_t *ps, lws_dlo_t *dlo) { const char *pname; /* Deal with ID matching */ pname = lws_html_get_atr(ps, "id", 2); if (!pname) return; lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ctx->ids)) { lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); if (!strcmp(pname, id->id)) { dlo->id = id; id->exists = 1; lwsl_debug("%s: %s tagged\n", __func__, pname); return; } } lws_end_foreach_dll(d); } lws_stateful_ret_t lws_lhp_parse(lhp_ctx_t *ctx, const uint8_t **buf, size_t *len) { lhp_pstack_t *ps1, *ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list); struct lws_context *cx = (struct lws_context *)ctx->user1; lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user; lws_stateful_ret_t r; const uint8_t *rbuf; size_t rsize; lhp_atr_t *a; if (ctx->await_css_done && !ctx->is_css) return LWS_SRET_AWAIT_RETRY; assert(drt); if (!*len && ctx->is_css && ctx->await_css_done && ctx->finish_css) goto finish_css; while (*len) { uint8_t c = *(*buf)++; (*len)--; if (ctx->state == LHPS_DO_START_ELEM) { /* we are retrying the inner callback */ (*len)++; (*buf)--; } // lwsl_notice("%s: %d, '%c', %02X\n", __func__, ctx->state, c, c); switch (ctx->state) { case LHPS_INIT: /* default css injection first, then... */ ctx->state = LCSPS_CSS_OUTER; ctx->u.f.default_css = 1; /* * recurse (there's no stack usage to speak of) to * do the default css parse first, CSS doesn't have a * way to recurse further. */ rbuf = (const uint8_t *)default_css; rsize = strlen(default_css); r = lws_lhp_parse(ctx, &rbuf, &rsize); if (r >= LWS_SRET_FATAL) { lwsl_err("%s: css parse fail\n", __func__); return r; } ctx->u.f.default_css = 0; ctx->npos = 0; ctx->state = LHPS_OUTER; /* fallthru */ case LHPS_OUTER: switch (c) { case '<': ctx->u.s = 0; ctx->u.f.first = 1; ctx->tag = NULL; ctx->tag_len = 0; ctx->state = LHPS_TAG; if (ctx->stack.count == LHP_MAX_ELEMS_NEST /* sanity */) { lwsl_err("%s: MAX_ELEMS_NEST\n", __func__); ps->cb(ctx, LHPCB_FAILED); return LWS_SRET_FATAL; } ps1 = lws_zalloc(sizeof(*ps1), __func__); if (!ps1) goto oom; /* inherit user and cb to start with */ ps1->user = ps->user; ps1->cb = ps->cb; lws_dll2_owner_clear(&ps1->atr); lws_dll2_add_tail(&ps1->list, &ctx->stack); ps = ps1; break; case '&': ctx->state = LHPS_AMP; ctx->temp_count = 0; continue; case '\t': case '\n': c = ' '; /* fallthru */ default: if (c != ' ' || !ctx->npos || ctx->buf[ctx->npos - 1] != ' ') ctx->buf[ctx->npos++] = (char)c; break; } if (ctx->npos && (ctx->state != LHPS_OUTER || ctx->npos >= LHP_STRING_CHUNK - 4)) { if (ctx->in_body && (ctx->npos != 1 || ctx->buf[0] != ' ')) { lws_css_cascade(ctx); ps->cb(ctx, LHPCB_CONTENT); } ctx->npos = 0; } break; case LHPS_TAG: if (c == '!' && ctx->u.f.first) { ctx->state = LHPS_SCOMMENT1; ctx->u.f.first = 0; break; } if (c == '/' && ctx->u.f.first) { /* remove the level we just prepared for this */ lhp_clean_level(ps); ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list); ctx->u.f.closing = 1; ctx->u.f.first = 0; break; } ctx->u.f.first = 0; /* it implies the end of the tag name */ if (hspace(c) || c == '/' || c == '>') { if (!ctx->u.f.tag_used && ctx->npos && !ctx->u.f.closing) { a = lhp_atr_new(ctx, (size_t)ctx->npos, 0); if (!a) goto oom; ctx->tag = (const char *)&a[1]; ctx->tag_len = a->name_len; ctx->u.f.tag_used = 1; if (ctx->tag_len == 8 && !strncasecmp(ctx->buf, "!doctype", 8)) ctx->u.f.doctype = 1; } if (c != '/' && c != '>') { /* after that, there may be attributes */ ctx->state = LHPS_ATTRIB; break; } /*