1 #include "html.h"
2 #include "html_tag.h"
3 #include "document.h"
4 #include "iterators.h"
5 #include "stylesheet.h"
6 #include "table.h"
7 #include <algorithm>
8 #include <locale>
9 #include "el_before_after.h"
10 #include "num_cvt.h"
11
html_tag(const std::shared_ptr<litehtml::document> & doc)12 litehtml::html_tag::html_tag(const std::shared_ptr<litehtml::document>& doc) : litehtml::element(doc)
13 {
14 m_box_sizing = box_sizing_content_box;
15 m_z_index = 0;
16 m_overflow = overflow_visible;
17 m_box = 0;
18 m_text_align = text_align_left;
19 m_el_position = element_position_static;
20 m_display = display_inline;
21 m_vertical_align = va_baseline;
22 m_list_style_type = list_style_type_none;
23 m_list_style_position = list_style_position_outside;
24 m_float = float_none;
25 m_clear = clear_none;
26 m_font = 0;
27 m_font_size = 0;
28 m_white_space = white_space_normal;
29 m_lh_predefined = false;
30 m_line_height = 0;
31 m_visibility = visibility_visible;
32 m_border_spacing_x = 0;
33 m_border_spacing_y = 0;
34 m_border_collapse = border_collapse_separate;
35 }
36
~html_tag()37 litehtml::html_tag::~html_tag()
38 {
39
40 }
41
appendChild(const element::ptr & el)42 bool litehtml::html_tag::appendChild(const element::ptr &el)
43 {
44 if(el)
45 {
46 el->parent(shared_from_this());
47 m_children.push_back(el);
48 return true;
49 }
50 return false;
51 }
52
removeChild(const element::ptr & el)53 bool litehtml::html_tag::removeChild(const element::ptr &el)
54 {
55 if(el && el->parent() == shared_from_this())
56 {
57 el->parent(nullptr);
58 m_children.erase(std::remove(m_children.begin(), m_children.end(), el), m_children.end());
59 return true;
60 }
61 return false;
62 }
63
clearRecursive()64 void litehtml::html_tag::clearRecursive()
65 {
66 for(auto& el : m_children)
67 {
68 el->clearRecursive();
69 el->parent(nullptr);
70 }
71 m_children.clear();
72 }
73
74
get_tagName() const75 const litehtml::tchar_t* litehtml::html_tag::get_tagName() const
76 {
77 return m_tag.c_str();
78 }
79
set_attr(const tchar_t * name,const tchar_t * val)80 void litehtml::html_tag::set_attr( const tchar_t* name, const tchar_t* val )
81 {
82 if(name && val)
83 {
84 tstring s_val = name;
85 for(size_t i = 0; i < s_val.length(); i++)
86 {
87 s_val[i] = std::tolower(s_val[i], std::locale::classic());
88 }
89 m_attrs[s_val] = val;
90
91 if( t_strcasecmp( name, _t("class") ) == 0 )
92 {
93 m_class_values.resize( 0 );
94 split_string( val, m_class_values, _t(" ") );
95 }
96 }
97 }
98
get_attr(const tchar_t * name,const tchar_t * def) const99 const litehtml::tchar_t* litehtml::html_tag::get_attr( const tchar_t* name, const tchar_t* def ) const
100 {
101 string_map::const_iterator attr = m_attrs.find(name);
102 if(attr != m_attrs.end())
103 {
104 return attr->second.c_str();
105 }
106 return def;
107 }
108
select_all(const tstring & selector)109 litehtml::elements_vector litehtml::html_tag::select_all( const tstring& selector )
110 {
111 css_selector sel(media_query_list::ptr(0));
112 sel.parse(selector);
113
114 return select_all(sel);
115 }
116
select_all(const css_selector & selector)117 litehtml::elements_vector litehtml::html_tag::select_all( const css_selector& selector )
118 {
119 litehtml::elements_vector res;
120 select_all(selector, res);
121 return res;
122 }
123
select_all(const css_selector & selector,elements_vector & res)124 void litehtml::html_tag::select_all(const css_selector& selector, elements_vector& res)
125 {
126 if(select(selector))
127 {
128 res.push_back(shared_from_this());
129 }
130
131 for(auto& el : m_children)
132 {
133 el->select_all(selector, res);
134 }
135 }
136
137
select_one(const tstring & selector)138 litehtml::element::ptr litehtml::html_tag::select_one( const tstring& selector )
139 {
140 css_selector sel(media_query_list::ptr(0));
141 sel.parse(selector);
142
143 return select_one(sel);
144 }
145
select_one(const css_selector & selector)146 litehtml::element::ptr litehtml::html_tag::select_one( const css_selector& selector )
147 {
148 if(select(selector))
149 {
150 return shared_from_this();
151 }
152
153 for(auto& el : m_children)
154 {
155 element::ptr res = el->select_one(selector);
156 if(res)
157 {
158 return res;
159 }
160 }
161 return 0;
162 }
163
apply_stylesheet(const litehtml::css & stylesheet)164 void litehtml::html_tag::apply_stylesheet( const litehtml::css& stylesheet )
165 {
166 remove_before_after();
167
168 for(const auto& sel : stylesheet.selectors())
169 {
170 int apply = select(*sel, false);
171
172 if(apply != select_no_match)
173 {
174 used_selector::ptr us = std::unique_ptr<used_selector>(new used_selector(sel, false));
175
176 if(sel->is_media_valid())
177 {
178 if(apply & select_match_pseudo_class)
179 {
180 if(select(*sel, true))
181 {
182 if(apply & select_match_with_after)
183 {
184 element::ptr el = get_element_after();
185 if(el)
186 {
187 el->add_style(*sel->m_style);
188 }
189 } else if(apply & select_match_with_before)
190 {
191 element::ptr el = get_element_before();
192 if(el)
193 {
194 el->add_style(*sel->m_style);
195 }
196 }
197 else
198 {
199 add_style(*sel->m_style);
200 us->m_used = true;
201 }
202 }
203 } else if(apply & select_match_with_after)
204 {
205 element::ptr el = get_element_after();
206 if(el)
207 {
208 el->add_style(*sel->m_style);
209 }
210 } else if(apply & select_match_with_before)
211 {
212 element::ptr el = get_element_before();
213 if(el)
214 {
215 el->add_style(*sel->m_style);
216 }
217 } else
218 {
219 add_style(*sel->m_style);
220 us->m_used = true;
221 }
222 }
223 m_used_styles.push_back(std::move(us));
224 }
225 }
226
227 for(auto& el : m_children)
228 {
229 if(el->get_display() != display_inline_text)
230 {
231 el->apply_stylesheet(stylesheet);
232 }
233 }
234 }
235
get_content_size(size & sz,int max_width)236 void litehtml::html_tag::get_content_size( size& sz, int max_width )
237 {
238 sz.height = 0;
239 if(m_display == display_block)
240 {
241 sz.width = max_width;
242 } else
243 {
244 sz.width = 0;
245 }
246 }
247
draw(uint_ptr hdc,int x,int y,const position * clip)248 void litehtml::html_tag::draw( uint_ptr hdc, int x, int y, const position* clip )
249 {
250 position pos = m_pos;
251 pos.x += x;
252 pos.y += y;
253
254 draw_background(hdc, x, y, clip);
255
256 if(m_display == display_list_item && m_list_style_type != list_style_type_none)
257 {
258 if(m_overflow > overflow_visible)
259 {
260 position border_box = pos;
261 border_box += m_padding;
262 border_box += m_borders;
263
264 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
265
266 bdr_radius -= m_borders;
267 bdr_radius -= m_padding;
268
269 get_document()->container()->set_clip(pos, bdr_radius, true, true);
270 }
271
272 draw_list_marker(hdc, pos);
273
274 if(m_overflow > overflow_visible)
275 {
276 get_document()->container()->del_clip();
277 }
278 }
279 }
280
get_font(font_metrics * fm)281 litehtml::uint_ptr litehtml::html_tag::get_font(font_metrics* fm)
282 {
283 if(fm)
284 {
285 *fm = m_font_metrics;
286 }
287 return m_font;
288 }
289
get_style_property(const tchar_t * name,bool inherited,const tchar_t * def)290 const litehtml::tchar_t* litehtml::html_tag::get_style_property( const tchar_t* name, bool inherited, const tchar_t* def /*= 0*/ )
291 {
292 const tchar_t* ret = m_style.get_property(name);
293 element::ptr el_parent = parent();
294 if (el_parent)
295 {
296 if ( ( ret && !t_strcasecmp(ret, _t("inherit")) ) || (!ret && inherited) )
297 {
298 ret = el_parent->get_style_property(name, inherited, def);
299 }
300 }
301
302 if(!ret)
303 {
304 ret = def;
305 }
306
307 return ret;
308 }
309
parse_styles(bool is_reparse)310 void litehtml::html_tag::parse_styles(bool is_reparse)
311 {
312 const tchar_t* style = get_attr(_t("style"));
313
314 if(style)
315 {
316 m_style.add(style, NULL);
317 }
318
319 init_font();
320 document::ptr doc = get_document();
321
322 m_el_position = (element_position) value_index(get_style_property(_t("position"), false, _t("static")), element_position_strings, element_position_fixed);
323 m_text_align = (text_align) value_index(get_style_property(_t("text-align"), true, _t("left")), text_align_strings, text_align_left);
324 m_overflow = (overflow) value_index(get_style_property(_t("overflow"), false, _t("visible")), overflow_strings, overflow_visible);
325 m_white_space = (white_space) value_index(get_style_property(_t("white-space"), true, _t("normal")), white_space_strings, white_space_normal);
326 m_display = (style_display) value_index(get_style_property(_t("display"), false, _t("inline")), style_display_strings, display_inline);
327 m_visibility = (visibility) value_index(get_style_property(_t("visibility"), true, _t("visible")), visibility_strings, visibility_visible);
328 m_box_sizing = (box_sizing) value_index(get_style_property(_t("box-sizing"), false, _t("content-box")), box_sizing_strings, box_sizing_content_box);
329
330 if(m_el_position != element_position_static)
331 {
332 const tchar_t* val = get_style_property(_t("z-index"), false, 0);
333 if(val)
334 {
335 m_z_index = t_atoi(val);
336 }
337 }
338
339 const tchar_t* va = get_style_property(_t("vertical-align"), true, _t("baseline"));
340 m_vertical_align = (vertical_align) value_index(va, vertical_align_strings, va_baseline);
341
342 const tchar_t* fl = get_style_property(_t("float"), false, _t("none"));
343 m_float = (element_float) value_index(fl, element_float_strings, float_none);
344
345 m_clear = (element_clear) value_index(get_style_property(_t("clear"), false, _t("none")), element_clear_strings, clear_none);
346
347 if (m_float != float_none)
348 {
349 // reset display in to block for floating elements
350 if (m_display != display_none)
351 {
352 m_display = display_block;
353 }
354 }
355 else if (m_display == display_table ||
356 m_display == display_inline_table ||
357 m_display == display_table_caption ||
358 m_display == display_table_cell ||
359 m_display == display_table_column ||
360 m_display == display_table_column_group ||
361 m_display == display_table_footer_group ||
362 m_display == display_table_header_group ||
363 m_display == display_table_row ||
364 m_display == display_table_row_group)
365 {
366 doc->add_tabular(shared_from_this());
367 }
368 // fix inline boxes with absolute/fixed positions
369 else if (m_display != display_none && is_inline_box())
370 {
371 if (m_el_position == element_position_absolute || m_el_position == element_position_fixed)
372 {
373 m_display = display_block;
374 }
375 }
376
377 m_css_text_indent.fromString( get_style_property(_t("text-indent"), true, _t("0")), _t("0"));
378
379 m_css_width.fromString( get_style_property(_t("width"), false, _t("auto")), _t("auto"));
380 m_css_height.fromString( get_style_property(_t("height"), false, _t("auto")), _t("auto"));
381
382 doc->cvt_units(m_css_width, m_font_size);
383 doc->cvt_units(m_css_height, m_font_size);
384
385 m_css_min_width.fromString( get_style_property(_t("min-width"), false, _t("0")));
386 m_css_min_height.fromString( get_style_property(_t("min-height"), false, _t("0")));
387
388 m_css_max_width.fromString( get_style_property(_t("max-width"), false, _t("none")), _t("none"));
389 m_css_max_height.fromString( get_style_property(_t("max-height"), false, _t("none")), _t("none"));
390
391 doc->cvt_units(m_css_min_width, m_font_size);
392 doc->cvt_units(m_css_min_height, m_font_size);
393
394 m_css_offsets.left.fromString( get_style_property(_t("left"), false, _t("auto")), _t("auto"));
395 m_css_offsets.right.fromString( get_style_property(_t("right"), false, _t("auto")), _t("auto"));
396 m_css_offsets.top.fromString( get_style_property(_t("top"), false, _t("auto")), _t("auto"));
397 m_css_offsets.bottom.fromString( get_style_property(_t("bottom"), false, _t("auto")), _t("auto"));
398
399 doc->cvt_units(m_css_offsets.left, m_font_size);
400 doc->cvt_units(m_css_offsets.right, m_font_size);
401 doc->cvt_units(m_css_offsets.top, m_font_size);
402 doc->cvt_units(m_css_offsets.bottom, m_font_size);
403
404 m_css_margins.left.fromString( get_style_property(_t("margin-left"), false, _t("0")), _t("auto"));
405 m_css_margins.right.fromString( get_style_property(_t("margin-right"), false, _t("0")), _t("auto"));
406 m_css_margins.top.fromString( get_style_property(_t("margin-top"), false, _t("0")), _t("auto"));
407 m_css_margins.bottom.fromString( get_style_property(_t("margin-bottom"), false, _t("0")), _t("auto"));
408
409 m_css_padding.left.fromString( get_style_property(_t("padding-left"), false, _t("0")), _t(""));
410 m_css_padding.right.fromString( get_style_property(_t("padding-right"), false, _t("0")), _t(""));
411 m_css_padding.top.fromString( get_style_property(_t("padding-top"), false, _t("0")), _t(""));
412 m_css_padding.bottom.fromString( get_style_property(_t("padding-bottom"), false, _t("0")), _t(""));
413
414 m_css_borders.left.width.fromString( get_style_property(_t("border-left-width"), false, _t("medium")), border_width_strings);
415 m_css_borders.right.width.fromString( get_style_property(_t("border-right-width"), false, _t("medium")), border_width_strings);
416 m_css_borders.top.width.fromString( get_style_property(_t("border-top-width"), false, _t("medium")), border_width_strings);
417 m_css_borders.bottom.width.fromString( get_style_property(_t("border-bottom-width"), false, _t("medium")), border_width_strings);
418
419 m_css_borders.left.color = web_color::from_string(get_style_property(_t("border-left-color"), false, _t("")), doc->container());
420 m_css_borders.left.style = (border_style) value_index(get_style_property(_t("border-left-style"), false, _t("none")), border_style_strings, border_style_none);
421
422 m_css_borders.right.color = web_color::from_string(get_style_property(_t("border-right-color"), false, _t("")), doc->container());
423 m_css_borders.right.style = (border_style) value_index(get_style_property(_t("border-right-style"), false, _t("none")), border_style_strings, border_style_none);
424
425 m_css_borders.top.color = web_color::from_string(get_style_property(_t("border-top-color"), false, _t("")), doc->container());
426 m_css_borders.top.style = (border_style) value_index(get_style_property(_t("border-top-style"), false, _t("none")), border_style_strings, border_style_none);
427
428 m_css_borders.bottom.color = web_color::from_string(get_style_property(_t("border-bottom-color"), false, _t("")), doc->container());
429 m_css_borders.bottom.style = (border_style) value_index(get_style_property(_t("border-bottom-style"), false, _t("none")), border_style_strings, border_style_none);
430
431 m_css_borders.radius.top_left_x.fromString(get_style_property(_t("border-top-left-radius-x"), false, _t("0")));
432 m_css_borders.radius.top_left_y.fromString(get_style_property(_t("border-top-left-radius-y"), false, _t("0")));
433
434 m_css_borders.radius.top_right_x.fromString(get_style_property(_t("border-top-right-radius-x"), false, _t("0")));
435 m_css_borders.radius.top_right_y.fromString(get_style_property(_t("border-top-right-radius-y"), false, _t("0")));
436
437 m_css_borders.radius.bottom_right_x.fromString(get_style_property(_t("border-bottom-right-radius-x"), false, _t("0")));
438 m_css_borders.radius.bottom_right_y.fromString(get_style_property(_t("border-bottom-right-radius-y"), false, _t("0")));
439
440 m_css_borders.radius.bottom_left_x.fromString(get_style_property(_t("border-bottom-left-radius-x"), false, _t("0")));
441 m_css_borders.radius.bottom_left_y.fromString(get_style_property(_t("border-bottom-left-radius-y"), false, _t("0")));
442
443 doc->cvt_units(m_css_borders.radius.bottom_left_x, m_font_size);
444 doc->cvt_units(m_css_borders.radius.bottom_left_y, m_font_size);
445 doc->cvt_units(m_css_borders.radius.bottom_right_x, m_font_size);
446 doc->cvt_units(m_css_borders.radius.bottom_right_y, m_font_size);
447 doc->cvt_units(m_css_borders.radius.top_left_x, m_font_size);
448 doc->cvt_units(m_css_borders.radius.top_left_y, m_font_size);
449 doc->cvt_units(m_css_borders.radius.top_right_x, m_font_size);
450 doc->cvt_units(m_css_borders.radius.top_right_y, m_font_size);
451
452 doc->cvt_units(m_css_text_indent, m_font_size);
453
454 m_margins.left = doc->cvt_units(m_css_margins.left, m_font_size);
455 m_margins.right = doc->cvt_units(m_css_margins.right, m_font_size);
456 m_margins.top = doc->cvt_units(m_css_margins.top, m_font_size);
457 m_margins.bottom = doc->cvt_units(m_css_margins.bottom, m_font_size);
458
459 m_padding.left = doc->cvt_units(m_css_padding.left, m_font_size);
460 m_padding.right = doc->cvt_units(m_css_padding.right, m_font_size);
461 m_padding.top = doc->cvt_units(m_css_padding.top, m_font_size);
462 m_padding.bottom = doc->cvt_units(m_css_padding.bottom, m_font_size);
463
464 m_borders.left = doc->cvt_units(m_css_borders.left.width, m_font_size);
465 m_borders.right = doc->cvt_units(m_css_borders.right.width, m_font_size);
466 m_borders.top = doc->cvt_units(m_css_borders.top.width, m_font_size);
467 m_borders.bottom = doc->cvt_units(m_css_borders.bottom.width, m_font_size);
468
469 css_length line_height;
470 line_height.fromString(get_style_property(_t("line-height"), true, _t("normal")), _t("normal"));
471 if(line_height.is_predefined())
472 {
473 m_line_height = m_font_metrics.height;
474 m_lh_predefined = true;
475 } else if(line_height.units() == css_units_none)
476 {
477 m_line_height = (int) (line_height.val() * m_font_size);
478 m_lh_predefined = false;
479 } else
480 {
481 m_line_height = doc->cvt_units(line_height, m_font_size, m_font_size);
482 m_lh_predefined = false;
483 }
484
485
486 if(m_display == display_list_item)
487 {
488 const tchar_t* list_type = get_style_property(_t("list-style-type"), true, _t("disc"));
489 m_list_style_type = (list_style_type) value_index(list_type, list_style_type_strings, list_style_type_disc);
490
491 const tchar_t* list_pos = get_style_property(_t("list-style-position"), true, _t("outside"));
492 m_list_style_position = (list_style_position) value_index(list_pos, list_style_position_strings, list_style_position_outside);
493
494 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
495 if(list_image && list_image[0])
496 {
497 tstring url;
498 css::parse_css_url(list_image, url);
499
500 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
501 doc->container()->load_image(url.c_str(), list_image_baseurl, true);
502 }
503
504 }
505
506 parse_background();
507
508 if(!is_reparse)
509 {
510 for(auto& el : m_children)
511 {
512 el->parse_styles();
513 }
514 }
515 }
516
render(int x,int y,int max_width,bool second_pass)517 int litehtml::html_tag::render( int x, int y, int max_width, bool second_pass )
518 {
519 if (m_display == display_table || m_display == display_inline_table)
520 {
521 return render_table(x, y, max_width, second_pass);
522 }
523
524 return render_box(x, y, max_width, second_pass);
525 }
526
is_white_space() const527 bool litehtml::html_tag::is_white_space() const
528 {
529 return false;
530 }
531
get_font_size() const532 int litehtml::html_tag::get_font_size() const
533 {
534 return m_font_size;
535 }
536
get_base_line()537 int litehtml::html_tag::get_base_line()
538 {
539 if(is_replaced())
540 {
541 return 0;
542 }
543 int bl = 0;
544 if(!m_boxes.empty())
545 {
546 bl = m_boxes.back()->baseline() + content_margins_bottom();
547 }
548 return bl;
549 }
550
init()551 void litehtml::html_tag::init()
552 {
553 if (m_display == display_table || m_display == display_inline_table)
554 {
555 if (m_grid)
556 {
557 m_grid->clear();
558 }
559 else
560 {
561 m_grid = std::unique_ptr<table_grid>(new table_grid());
562 }
563
564 go_inside_table table_selector;
565 table_rows_selector row_selector;
566 table_cells_selector cell_selector;
567
568 elements_iterator row_iter(shared_from_this(), &table_selector, &row_selector);
569
570 element::ptr row = row_iter.next(false);
571 while (row)
572 {
573 m_grid->begin_row(row);
574
575 elements_iterator cell_iter(row, &table_selector, &cell_selector);
576 element::ptr cell = cell_iter.next();
577 while (cell)
578 {
579 m_grid->add_cell(cell);
580
581 cell = cell_iter.next(false);
582 }
583 row = row_iter.next(false);
584 }
585
586 m_grid->finish();
587 }
588
589 for (auto& el : m_children)
590 {
591 el->init();
592 }
593 }
594
select(const css_selector & selector,bool apply_pseudo)595 int litehtml::html_tag::select(const css_selector& selector, bool apply_pseudo)
596 {
597 int right_res = select(selector.m_right, apply_pseudo);
598 if(right_res == select_no_match)
599 {
600 return select_no_match;
601 }
602 element::ptr el_parent = parent();
603 if(selector.m_left)
604 {
605 if (!el_parent)
606 {
607 return select_no_match;
608 }
609 switch(selector.m_combinator)
610 {
611 case combinator_descendant:
612 {
613 bool is_pseudo = false;
614 element::ptr res = find_ancestor(*selector.m_left, apply_pseudo, &is_pseudo);
615 if(!res)
616 {
617 return select_no_match;
618 } else
619 {
620 if(is_pseudo)
621 {
622 right_res |= select_match_pseudo_class;
623 }
624 }
625 }
626 break;
627 case combinator_child:
628 {
629 int res = el_parent->select(*selector.m_left, apply_pseudo);
630 if(res == select_no_match)
631 {
632 return select_no_match;
633 } else
634 {
635 if(right_res != select_match_pseudo_class)
636 {
637 right_res |= res;
638 }
639 }
640 }
641 break;
642 case combinator_adjacent_sibling:
643 {
644 bool is_pseudo = false;
645 element::ptr res = el_parent->find_adjacent_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
646 if(!res)
647 {
648 return select_no_match;
649 } else
650 {
651 if(is_pseudo)
652 {
653 right_res |= select_match_pseudo_class;
654 }
655 }
656 }
657 break;
658 case combinator_general_sibling:
659 {
660 bool is_pseudo = false;
661 element::ptr res = el_parent->find_sibling(shared_from_this(), *selector.m_left, apply_pseudo, &is_pseudo);
662 if(!res)
663 {
664 return select_no_match;
665 } else
666 {
667 if(is_pseudo)
668 {
669 right_res |= select_match_pseudo_class;
670 }
671 }
672 }
673 break;
674 default:
675 right_res = select_no_match;
676 }
677 }
678 return right_res;
679 }
680
select(const css_element_selector & selector,bool apply_pseudo)681 int litehtml::html_tag::select(const css_element_selector& selector, bool apply_pseudo)
682 {
683 if(!selector.m_tag.empty() && selector.m_tag != _t("*"))
684 {
685 if(selector.m_tag != m_tag)
686 {
687 return select_no_match;
688 }
689 }
690
691 int res = select_match;
692 element::ptr el_parent = parent();
693
694 for(css_attribute_selector::vector::const_iterator i = selector.m_attrs.begin(); i != selector.m_attrs.end(); i++)
695 {
696 const tchar_t* attr_value = get_attr(i->attribute.c_str());
697 switch(i->condition)
698 {
699 case select_exists:
700 if(!attr_value)
701 {
702 return select_no_match;
703 }
704 break;
705 case select_equal:
706 if(!attr_value)
707 {
708 return select_no_match;
709 } else
710 {
711 if(i->attribute == _t("class"))
712 {
713 const string_vector & tokens1 = m_class_values;
714 const string_vector & tokens2 = i->class_val;
715 bool found = true;
716 for(string_vector::const_iterator str1 = tokens2.begin(); str1 != tokens2.end() && found; str1++)
717 {
718 bool f = false;
719 for(string_vector::const_iterator str2 = tokens1.begin(); str2 != tokens1.end() && !f; str2++)
720 {
721 if( !t_strcasecmp(str1->c_str(), str2->c_str()) )
722 {
723 f = true;
724 }
725 }
726 if(!f)
727 {
728 found = false;
729 }
730 }
731 if(!found)
732 {
733 return select_no_match;
734 }
735 } else
736 {
737 if( t_strcasecmp(i->val.c_str(), attr_value) )
738 {
739 return select_no_match;
740 }
741 }
742 }
743 break;
744 case select_contain_str:
745 if(!attr_value)
746 {
747 return select_no_match;
748 } else if(!t_strstr(attr_value, i->val.c_str()))
749 {
750 return select_no_match;
751 }
752 break;
753 case select_start_str:
754 if(!attr_value)
755 {
756 return select_no_match;
757 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
758 {
759 return select_no_match;
760 }
761 break;
762 case select_end_str:
763 if(!attr_value)
764 {
765 return select_no_match;
766 } else if(t_strncmp(attr_value, i->val.c_str(), i->val.length()))
767 {
768 const tchar_t* s = attr_value + t_strlen(attr_value) - i->val.length() - 1;
769 if(s < attr_value)
770 {
771 return select_no_match;
772 }
773 if(i->val != s)
774 {
775 return select_no_match;
776 }
777 }
778 break;
779 case select_pseudo_element:
780 if(i->val == _t("after"))
781 {
782 res |= select_match_with_after;
783 } else if(i->val == _t("before"))
784 {
785 res |= select_match_with_before;
786 } else
787 {
788 return select_no_match;
789 }
790 break;
791 case select_pseudo_class:
792 if(apply_pseudo)
793 {
794 if (!el_parent) return select_no_match;
795
796 tstring selector_param;
797 tstring selector_name;
798
799 tstring::size_type begin = i->val.find_first_of(_t('('));
800 tstring::size_type end = (begin == tstring::npos) ? tstring::npos : find_close_bracket(i->val, begin);
801 if(begin != tstring::npos && end != tstring::npos)
802 {
803 selector_param = i->val.substr(begin + 1, end - begin - 1);
804 }
805 if(begin != tstring::npos)
806 {
807 selector_name = i->val.substr(0, begin);
808 litehtml::trim(selector_name);
809 } else
810 {
811 selector_name = i->val;
812 }
813
814 int selector = value_index(selector_name.c_str(), pseudo_class_strings);
815
816 switch(selector)
817 {
818 case pseudo_class_only_child:
819 if (!el_parent->is_only_child(shared_from_this(), false))
820 {
821 return select_no_match;
822 }
823 break;
824 case pseudo_class_only_of_type:
825 if (!el_parent->is_only_child(shared_from_this(), true))
826 {
827 return select_no_match;
828 }
829 break;
830 case pseudo_class_first_child:
831 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, false))
832 {
833 return select_no_match;
834 }
835 break;
836 case pseudo_class_first_of_type:
837 if (!el_parent->is_nth_child(shared_from_this(), 0, 1, true))
838 {
839 return select_no_match;
840 }
841 break;
842 case pseudo_class_last_child:
843 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, false))
844 {
845 return select_no_match;
846 }
847 break;
848 case pseudo_class_last_of_type:
849 if (!el_parent->is_nth_last_child(shared_from_this(), 0, 1, true))
850 {
851 return select_no_match;
852 }
853 break;
854 case pseudo_class_nth_child:
855 case pseudo_class_nth_of_type:
856 case pseudo_class_nth_last_child:
857 case pseudo_class_nth_last_of_type:
858 {
859 if(selector_param.empty()) return select_no_match;
860
861 int num = 0;
862 int off = 0;
863
864 parse_nth_child_params(selector_param, num, off);
865 if(!num && !off) return select_no_match;
866 switch(selector)
867 {
868 case pseudo_class_nth_child:
869 if (!el_parent->is_nth_child(shared_from_this(), num, off, false))
870 {
871 return select_no_match;
872 }
873 break;
874 case pseudo_class_nth_of_type:
875 if (!el_parent->is_nth_child(shared_from_this(), num, off, true))
876 {
877 return select_no_match;
878 }
879 break;
880 case pseudo_class_nth_last_child:
881 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, false))
882 {
883 return select_no_match;
884 }
885 break;
886 case pseudo_class_nth_last_of_type:
887 if (!el_parent->is_nth_last_child(shared_from_this(), num, off, true))
888 {
889 return select_no_match;
890 }
891 break;
892 }
893
894 }
895 break;
896 case pseudo_class_not:
897 {
898 css_element_selector sel;
899 sel.parse(selector_param);
900 if(select(sel, apply_pseudo))
901 {
902 return select_no_match;
903 }
904 }
905 break;
906 case pseudo_class_lang:
907 {
908 trim( selector_param );
909
910 if( !get_document()->match_lang( selector_param ) )
911 {
912 return select_no_match;
913 }
914 }
915 break;
916 default:
917 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), i->val) == m_pseudo_classes.end())
918 {
919 return select_no_match;
920 }
921 break;
922 }
923 } else
924 {
925 res |= select_match_pseudo_class;
926 }
927 break;
928 }
929 }
930 return res;
931 }
932
find_ancestor(const css_selector & selector,bool apply_pseudo,bool * is_pseudo)933 litehtml::element::ptr litehtml::html_tag::find_ancestor(const css_selector& selector, bool apply_pseudo, bool* is_pseudo)
934 {
935 element::ptr el_parent = parent();
936 if (!el_parent)
937 {
938 return nullptr;
939 }
940 int res = el_parent->select(selector, apply_pseudo);
941 if(res != select_no_match)
942 {
943 if(is_pseudo)
944 {
945 if(res & select_match_pseudo_class)
946 {
947 *is_pseudo = true;
948 } else
949 {
950 *is_pseudo = false;
951 }
952 }
953 return el_parent;
954 }
955 return el_parent->find_ancestor(selector, apply_pseudo, is_pseudo);
956 }
957
get_floats_height(element_float el_float) const958 int litehtml::html_tag::get_floats_height(element_float el_float) const
959 {
960 if(is_floats_holder())
961 {
962 int h = 0;
963
964 bool process = false;
965
966 for(const auto& fb : m_floats_left)
967 {
968 process = false;
969 switch(el_float)
970 {
971 case float_none:
972 process = true;
973 break;
974 case float_left:
975 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
976 {
977 process = true;
978 }
979 break;
980 case float_right:
981 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
982 {
983 process = true;
984 }
985 break;
986 }
987 if(process)
988 {
989 if(el_float == float_none)
990 {
991 h = std::max(h, fb.pos.bottom());
992 } else
993 {
994 h = std::max(h, fb.pos.top());
995 }
996 }
997 }
998
999
1000 for(const auto fb : m_floats_right)
1001 {
1002 process = false;
1003 switch(el_float)
1004 {
1005 case float_none:
1006 process = true;
1007 break;
1008 case float_left:
1009 if (fb.clear_floats == clear_left || fb.clear_floats == clear_both)
1010 {
1011 process = true;
1012 }
1013 break;
1014 case float_right:
1015 if (fb.clear_floats == clear_right || fb.clear_floats == clear_both)
1016 {
1017 process = true;
1018 }
1019 break;
1020 }
1021 if(process)
1022 {
1023 if(el_float == float_none)
1024 {
1025 h = std::max(h, fb.pos.bottom());
1026 } else
1027 {
1028 h = std::max(h, fb.pos.top());
1029 }
1030 }
1031 }
1032
1033 return h;
1034 }
1035 element::ptr el_parent = parent();
1036 if (el_parent)
1037 {
1038 int h = el_parent->get_floats_height(el_float);
1039 return h - m_pos.y;
1040 }
1041 return 0;
1042 }
1043
get_left_floats_height() const1044 int litehtml::html_tag::get_left_floats_height() const
1045 {
1046 if(is_floats_holder())
1047 {
1048 int h = 0;
1049 if(!m_floats_left.empty())
1050 {
1051 for (const auto& fb : m_floats_left)
1052 {
1053 h = std::max(h, fb.pos.bottom());
1054 }
1055 }
1056 return h;
1057 }
1058 element::ptr el_parent = parent();
1059 if (el_parent)
1060 {
1061 int h = el_parent->get_left_floats_height();
1062 return h - m_pos.y;
1063 }
1064 return 0;
1065 }
1066
get_right_floats_height() const1067 int litehtml::html_tag::get_right_floats_height() const
1068 {
1069 if(is_floats_holder())
1070 {
1071 int h = 0;
1072 if(!m_floats_right.empty())
1073 {
1074 for(const auto& fb : m_floats_right)
1075 {
1076 h = std::max(h, fb.pos.bottom());
1077 }
1078 }
1079 return h;
1080 }
1081 element::ptr el_parent = parent();
1082 if (el_parent)
1083 {
1084 int h = el_parent->get_right_floats_height();
1085 return h - m_pos.y;
1086 }
1087 return 0;
1088 }
1089
get_line_left(int y)1090 int litehtml::html_tag::get_line_left( int y )
1091 {
1092 if(is_floats_holder())
1093 {
1094 if(m_cahe_line_left.is_valid && m_cahe_line_left.hash == y)
1095 {
1096 return m_cahe_line_left.val;
1097 }
1098
1099 int w = 0;
1100 for(const auto& fb : m_floats_left)
1101 {
1102 if (y >= fb.pos.top() && y < fb.pos.bottom())
1103 {
1104 w = std::max(w, fb.pos.right());
1105 if (w < fb.pos.right())
1106 {
1107 break;
1108 }
1109 }
1110 }
1111 m_cahe_line_left.set_value(y, w);
1112 return w;
1113 }
1114 element::ptr el_parent = parent();
1115 if (el_parent)
1116 {
1117 int w = el_parent->get_line_left(y + m_pos.y);
1118 if (w < 0)
1119 {
1120 w = 0;
1121 }
1122 return w - (w ? m_pos.x : 0);
1123 }
1124 return 0;
1125 }
1126
get_line_right(int y,int def_right)1127 int litehtml::html_tag::get_line_right( int y, int def_right )
1128 {
1129 if(is_floats_holder())
1130 {
1131 if(m_cahe_line_right.is_valid && m_cahe_line_right.hash == y)
1132 {
1133 if(m_cahe_line_right.is_default)
1134 {
1135 return def_right;
1136 } else
1137 {
1138 return std::min(m_cahe_line_right.val, def_right);
1139 }
1140 }
1141
1142 int w = def_right;
1143 m_cahe_line_right.is_default = true;
1144 for(const auto& fb : m_floats_right)
1145 {
1146 if(y >= fb.pos.top() && y < fb.pos.bottom())
1147 {
1148 w = std::min(w, fb.pos.left());
1149 m_cahe_line_right.is_default = false;
1150 if(w > fb.pos.left())
1151 {
1152 break;
1153 }
1154 }
1155 }
1156 m_cahe_line_right.set_value(y, w);
1157 return w;
1158 }
1159 element::ptr el_parent = parent();
1160 if (el_parent)
1161 {
1162 int w = el_parent->get_line_right(y + m_pos.y, def_right + m_pos.x);
1163 return w - m_pos.x;
1164 }
1165 return 0;
1166 }
1167
1168
get_line_left_right(int y,int def_right,int & ln_left,int & ln_right)1169 void litehtml::html_tag::get_line_left_right( int y, int def_right, int& ln_left, int& ln_right )
1170 {
1171 if(is_floats_holder())
1172 {
1173 ln_left = get_line_left(y);
1174 ln_right = get_line_right(y, def_right);
1175 } else
1176 {
1177 element::ptr el_parent = parent();
1178 if (el_parent)
1179 {
1180 el_parent->get_line_left_right(y + m_pos.y, def_right + m_pos.x, ln_left, ln_right);
1181 }
1182 ln_right -= m_pos.x;
1183
1184 if(ln_left < 0)
1185 {
1186 ln_left = 0;
1187 } else if (ln_left > 0)
1188 {
1189 ln_left -= m_pos.x;
1190 if (ln_left < 0)
1191 {
1192 ln_left = 0;
1193 }
1194 }
1195 }
1196 }
1197
fix_line_width(int max_width,element_float flt)1198 int litehtml::html_tag::fix_line_width( int max_width, element_float flt )
1199 {
1200 int ret_width = 0;
1201 if(!m_boxes.empty())
1202 {
1203 elements_vector els;
1204 m_boxes.back()->get_elements(els);
1205 bool was_cleared = false;
1206 if(!els.empty() && els.front()->get_clear() != clear_none)
1207 {
1208 if(els.front()->get_clear() == clear_both)
1209 {
1210 was_cleared = true;
1211 } else
1212 {
1213 if( (flt == float_left && els.front()->get_clear() == clear_left) ||
1214 (flt == float_right && els.front()->get_clear() == clear_right) )
1215 {
1216 was_cleared = true;
1217 }
1218 }
1219 }
1220
1221 if(!was_cleared)
1222 {
1223 m_boxes.pop_back();
1224
1225 for(elements_vector::iterator i = els.begin(); i != els.end(); i++)
1226 {
1227 int rw = place_element((*i), max_width);
1228 if(rw > ret_width)
1229 {
1230 ret_width = rw;
1231 }
1232 }
1233 } else
1234 {
1235 int line_top = 0;
1236 if(m_boxes.back()->get_type() == box_line)
1237 {
1238 line_top = m_boxes.back()->top();
1239 } else
1240 {
1241 line_top = m_boxes.back()->bottom();
1242 }
1243
1244 int line_left = 0;
1245 int line_right = max_width;
1246 get_line_left_right(line_top, max_width, line_left, line_right);
1247
1248 if(m_boxes.back()->get_type() == box_line)
1249 {
1250 if(m_boxes.size() == 1 && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
1251 {
1252 int sz_font = get_font_size();
1253 line_left += sz_font;
1254 }
1255
1256 if(m_css_text_indent.val() != 0)
1257 {
1258 bool line_box_found = false;
1259 for(box::vector::iterator iter = m_boxes.begin(); iter < m_boxes.end(); iter++)
1260 {
1261 if((*iter)->get_type() == box_line)
1262 {
1263 line_box_found = true;
1264 break;
1265 }
1266 }
1267 if(!line_box_found)
1268 {
1269 line_left += m_css_text_indent.calc_percent(max_width);
1270 }
1271 }
1272
1273 }
1274
1275 elements_vector els;
1276 m_boxes.back()->new_width(line_left, line_right, els);
1277 for(auto& el : els)
1278 {
1279 int rw = place_element(el, max_width);
1280 if(rw > ret_width)
1281 {
1282 ret_width = rw;
1283 }
1284 }
1285 }
1286 }
1287
1288 return ret_width;
1289 }
1290
add_float(const element::ptr & el,int x,int y)1291 void litehtml::html_tag::add_float(const element::ptr &el, int x, int y)
1292 {
1293 if(is_floats_holder())
1294 {
1295 floated_box fb;
1296 fb.pos.x = el->left() + x;
1297 fb.pos.y = el->top() + y;
1298 fb.pos.width = el->width();
1299 fb.pos.height = el->height();
1300 fb.float_side = el->get_float();
1301 fb.clear_floats = el->get_clear();
1302 fb.el = el;
1303
1304 if(fb.float_side == float_left)
1305 {
1306 if(m_floats_left.empty())
1307 {
1308 m_floats_left.push_back(fb);
1309 } else
1310 {
1311 bool inserted = false;
1312 for(floated_box::vector::iterator i = m_floats_left.begin(); i != m_floats_left.end(); i++)
1313 {
1314 if(fb.pos.right() > i->pos.right())
1315 {
1316 m_floats_left.insert(i, std::move(fb));
1317 inserted = true;
1318 break;
1319 }
1320 }
1321 if(!inserted)
1322 {
1323 m_floats_left.push_back(std::move(fb));
1324 }
1325 }
1326 m_cahe_line_left.invalidate();
1327 } else if(fb.float_side == float_right)
1328 {
1329 if(m_floats_right.empty())
1330 {
1331 m_floats_right.push_back(std::move(fb));
1332 } else
1333 {
1334 bool inserted = false;
1335 for(floated_box::vector::iterator i = m_floats_right.begin(); i != m_floats_right.end(); i++)
1336 {
1337 if(fb.pos.left() < i->pos.left())
1338 {
1339 m_floats_right.insert(i, std::move(fb));
1340 inserted = true;
1341 break;
1342 }
1343 }
1344 if(!inserted)
1345 {
1346 m_floats_right.push_back(fb);
1347 }
1348 }
1349 m_cahe_line_right.invalidate();
1350 }
1351 } else
1352 {
1353 element::ptr el_parent = parent();
1354 if (el_parent)
1355 {
1356 el_parent->add_float(el, x + m_pos.x, y + m_pos.y);
1357 }
1358 }
1359 }
1360
find_next_line_top(int top,int width,int def_right)1361 int litehtml::html_tag::find_next_line_top( int top, int width, int def_right )
1362 {
1363 if(is_floats_holder())
1364 {
1365 int new_top = top;
1366 int_vector points;
1367
1368 for(const auto& fb : m_floats_left)
1369 {
1370 if(fb.pos.top() >= top)
1371 {
1372 if(find(points.begin(), points.end(), fb.pos.top()) == points.end())
1373 {
1374 points.push_back(fb.pos.top());
1375 }
1376 }
1377 if (fb.pos.bottom() >= top)
1378 {
1379 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1380 {
1381 points.push_back(fb.pos.bottom());
1382 }
1383 }
1384 }
1385
1386 for (const auto& fb : m_floats_right)
1387 {
1388 if (fb.pos.top() >= top)
1389 {
1390 if (find(points.begin(), points.end(), fb.pos.top()) == points.end())
1391 {
1392 points.push_back(fb.pos.top());
1393 }
1394 }
1395 if (fb.pos.bottom() >= top)
1396 {
1397 if (find(points.begin(), points.end(), fb.pos.bottom()) == points.end())
1398 {
1399 points.push_back(fb.pos.bottom());
1400 }
1401 }
1402 }
1403
1404 if(!points.empty())
1405 {
1406 sort(points.begin(), points.end(), std::less<int>( ));
1407 new_top = points.back();
1408
1409 for(auto pt : points)
1410 {
1411 int pos_left = 0;
1412 int pos_right = def_right;
1413 get_line_left_right(pt, def_right, pos_left, pos_right);
1414
1415 if(pos_right - pos_left >= width)
1416 {
1417 new_top = pt;
1418 break;
1419 }
1420 }
1421 }
1422 return new_top;
1423 }
1424 element::ptr el_parent = parent();
1425 if (el_parent)
1426 {
1427 int new_top = el_parent->find_next_line_top(top + m_pos.y, width, def_right + m_pos.x);
1428 return new_top - m_pos.y;
1429 }
1430 return 0;
1431 }
1432
parse_background()1433 void litehtml::html_tag::parse_background()
1434 {
1435 // parse background-color
1436 m_bg.m_color = get_color(_t("background-color"), false, web_color(0, 0, 0, 0));
1437
1438 // parse background-position
1439 const tchar_t* str = get_style_property(_t("background-position"), false, _t("0% 0%"));
1440 if(str)
1441 {
1442 string_vector res;
1443 split_string(str, res, _t(" \t"));
1444 if(res.size() > 0)
1445 {
1446 if(res.size() == 1)
1447 {
1448 if( value_in_list(res[0].c_str(), _t("left;right;center")) )
1449 {
1450 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1451 m_bg.m_position.y.set_value(50, css_units_percentage);
1452 } else if( value_in_list(res[0].c_str(), _t("top;bottom;center")) )
1453 {
1454 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1455 m_bg.m_position.x.set_value(50, css_units_percentage);
1456 } else
1457 {
1458 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1459 m_bg.m_position.y.set_value(50, css_units_percentage);
1460 }
1461 } else
1462 {
1463 if(value_in_list(res[0].c_str(), _t("left;right")))
1464 {
1465 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1466 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1467 } else if(value_in_list(res[0].c_str(), _t("top;bottom")))
1468 {
1469 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1470 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1471 } else if(value_in_list(res[1].c_str(), _t("left;right")))
1472 {
1473 m_bg.m_position.x.fromString(res[1], _t("left;right;center"));
1474 m_bg.m_position.y.fromString(res[0], _t("top;bottom;center"));
1475 }else if(value_in_list(res[1].c_str(), _t("top;bottom")))
1476 {
1477 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1478 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1479 } else
1480 {
1481 m_bg.m_position.x.fromString(res[0], _t("left;right;center"));
1482 m_bg.m_position.y.fromString(res[1], _t("top;bottom;center"));
1483 }
1484 }
1485
1486 if(m_bg.m_position.x.is_predefined())
1487 {
1488 switch(m_bg.m_position.x.predef())
1489 {
1490 case 0:
1491 m_bg.m_position.x.set_value(0, css_units_percentage);
1492 break;
1493 case 1:
1494 m_bg.m_position.x.set_value(100, css_units_percentage);
1495 break;
1496 case 2:
1497 m_bg.m_position.x.set_value(50, css_units_percentage);
1498 break;
1499 }
1500 }
1501 if(m_bg.m_position.y.is_predefined())
1502 {
1503 switch(m_bg.m_position.y.predef())
1504 {
1505 case 0:
1506 m_bg.m_position.y.set_value(0, css_units_percentage);
1507 break;
1508 case 1:
1509 m_bg.m_position.y.set_value(100, css_units_percentage);
1510 break;
1511 case 2:
1512 m_bg.m_position.y.set_value(50, css_units_percentage);
1513 break;
1514 }
1515 }
1516 } else
1517 {
1518 m_bg.m_position.x.set_value(0, css_units_percentage);
1519 m_bg.m_position.y.set_value(0, css_units_percentage);
1520 }
1521 } else
1522 {
1523 m_bg.m_position.y.set_value(0, css_units_percentage);
1524 m_bg.m_position.x.set_value(0, css_units_percentage);
1525 }
1526
1527 str = get_style_property(_t("background-size"), false, _t("auto"));
1528 if(str)
1529 {
1530 string_vector res;
1531 split_string(str, res, _t(" \t"));
1532 if(!res.empty())
1533 {
1534 m_bg.m_position.width.fromString(res[0], background_size_strings);
1535 if(res.size() > 1)
1536 {
1537 m_bg.m_position.height.fromString(res[1], background_size_strings);
1538 } else
1539 {
1540 m_bg.m_position.height.predef(background_size_auto);
1541 }
1542 } else
1543 {
1544 m_bg.m_position.width.predef(background_size_auto);
1545 m_bg.m_position.height.predef(background_size_auto);
1546 }
1547 }
1548
1549 document::ptr doc = get_document();
1550
1551 doc->cvt_units(m_bg.m_position.x, m_font_size);
1552 doc->cvt_units(m_bg.m_position.y, m_font_size);
1553 doc->cvt_units(m_bg.m_position.width, m_font_size);
1554 doc->cvt_units(m_bg.m_position.height, m_font_size);
1555
1556 // parse background_attachment
1557 m_bg.m_attachment = (background_attachment) value_index(
1558 get_style_property(_t("background-attachment"), false, _t("scroll")),
1559 background_attachment_strings,
1560 background_attachment_scroll);
1561
1562 // parse background_attachment
1563 m_bg.m_repeat = (background_repeat) value_index(
1564 get_style_property(_t("background-repeat"), false, _t("repeat")),
1565 background_repeat_strings,
1566 background_repeat_repeat);
1567
1568 // parse background_clip
1569 m_bg.m_clip = (background_box) value_index(
1570 get_style_property(_t("background-clip"), false, _t("border-box")),
1571 background_box_strings,
1572 background_box_border);
1573
1574 // parse background_origin
1575 m_bg.m_origin = (background_box) value_index(
1576 get_style_property(_t("background-origin"), false, _t("padding-box")),
1577 background_box_strings,
1578 background_box_content);
1579
1580 // parse background-image
1581 css::parse_css_url(get_style_property(_t("background-image"), false, _t("")), m_bg.m_image);
1582 m_bg.m_baseurl = get_style_property(_t("background-image-baseurl"), false, _t(""));
1583
1584 if(!m_bg.m_image.empty())
1585 {
1586 doc->container()->load_image(m_bg.m_image.c_str(), m_bg.m_baseurl.empty() ? 0 : m_bg.m_baseurl.c_str(), true);
1587 }
1588 }
1589
add_positioned(const element::ptr & el)1590 void litehtml::html_tag::add_positioned(const element::ptr &el)
1591 {
1592 if (m_el_position != element_position_static || (!have_parent()))
1593 {
1594 m_positioned.push_back(el);
1595 } else
1596 {
1597 element::ptr el_parent = parent();
1598 if (el_parent)
1599 {
1600 el_parent->add_positioned(el);
1601 }
1602 }
1603 }
1604
calc_outlines(int parent_width)1605 void litehtml::html_tag::calc_outlines( int parent_width )
1606 {
1607 m_padding.left = m_css_padding.left.calc_percent(parent_width);
1608 m_padding.right = m_css_padding.right.calc_percent(parent_width);
1609
1610 m_borders.left = m_css_borders.left.width.calc_percent(parent_width);
1611 m_borders.right = m_css_borders.right.width.calc_percent(parent_width);
1612
1613 m_margins.left = m_css_margins.left.calc_percent(parent_width);
1614 m_margins.right = m_css_margins.right.calc_percent(parent_width);
1615
1616 m_margins.top = m_css_margins.top.calc_percent(parent_width);
1617 m_margins.bottom = m_css_margins.bottom.calc_percent(parent_width);
1618
1619 m_padding.top = m_css_padding.top.calc_percent(parent_width);
1620 m_padding.bottom = m_css_padding.bottom.calc_percent(parent_width);
1621 }
1622
calc_auto_margins(int parent_width)1623 void litehtml::html_tag::calc_auto_margins(int parent_width)
1624 {
1625 if (get_element_position() != element_position_absolute && (m_display == display_block || m_display == display_table))
1626 {
1627 if (m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1628 {
1629 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right;
1630 if (el_width <= parent_width)
1631 {
1632 m_margins.left = (parent_width - el_width) / 2;
1633 m_margins.right = (parent_width - el_width) - m_margins.left;
1634 }
1635 else
1636 {
1637 m_margins.left = 0;
1638 m_margins.right = 0;
1639 }
1640 }
1641 else if (m_css_margins.left.is_predefined() && !m_css_margins.right.is_predefined())
1642 {
1643 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.right;
1644 m_margins.left = parent_width - el_width;
1645 if (m_margins.left < 0) m_margins.left = 0;
1646 }
1647 else if (!m_css_margins.left.is_predefined() && m_css_margins.right.is_predefined())
1648 {
1649 int el_width = m_pos.width + m_borders.left + m_borders.right + m_padding.left + m_padding.right + m_margins.left;
1650 m_margins.right = parent_width - el_width;
1651 if (m_margins.right < 0) m_margins.right = 0;
1652 }
1653 }
1654 }
1655
parse_attributes()1656 void litehtml::html_tag::parse_attributes()
1657 {
1658 for(auto& el : m_children)
1659 {
1660 el->parse_attributes();
1661 }
1662 }
1663
get_text(tstring & text)1664 void litehtml::html_tag::get_text( tstring& text )
1665 {
1666 for (auto& el : m_children)
1667 {
1668 el->get_text(text);
1669 }
1670 }
1671
is_body() const1672 bool litehtml::html_tag::is_body() const
1673 {
1674 return false;
1675 }
1676
set_data(const tchar_t * data)1677 void litehtml::html_tag::set_data( const tchar_t* data )
1678 {
1679
1680 }
1681
get_inline_boxes(position::vector & boxes)1682 void litehtml::html_tag::get_inline_boxes( position::vector& boxes )
1683 {
1684 litehtml::box* old_box = 0;
1685 position pos;
1686 for(auto& el : m_children)
1687 {
1688 if(!el->skip())
1689 {
1690 if(el->m_box)
1691 {
1692 if(el->m_box != old_box)
1693 {
1694 if(old_box)
1695 {
1696 if(boxes.empty())
1697 {
1698 pos.x -= m_padding.left + m_borders.left;
1699 pos.width += m_padding.left + m_borders.left;
1700 }
1701 boxes.push_back(pos);
1702 }
1703 old_box = el->m_box;
1704 pos.x = el->left() + el->margin_left();
1705 pos.y = el->top() - m_padding.top - m_borders.top;
1706 pos.width = 0;
1707 pos.height = 0;
1708 }
1709 pos.width = el->right() - pos.x - el->margin_right() - el->margin_left();
1710 pos.height = std::max(pos.height, el->height() + m_padding.top + m_padding.bottom + m_borders.top + m_borders.bottom);
1711 } else if(el->get_display() == display_inline)
1712 {
1713 position::vector sub_boxes;
1714 el->get_inline_boxes(sub_boxes);
1715 if(!sub_boxes.empty())
1716 {
1717 sub_boxes.rbegin()->width += el->margin_right();
1718 if(boxes.empty())
1719 {
1720 if(m_padding.left + m_borders.left > 0)
1721 {
1722 position padding_box = (*sub_boxes.begin());
1723 padding_box.x -= m_padding.left + m_borders.left + el->margin_left();
1724 padding_box.width = m_padding.left + m_borders.left + el->margin_left();
1725 boxes.push_back(padding_box);
1726 }
1727 }
1728
1729 sub_boxes.rbegin()->width += el->margin_right();
1730
1731 boxes.insert(boxes.end(), sub_boxes.begin(), sub_boxes.end());
1732 }
1733 }
1734 }
1735 }
1736 if(pos.width || pos.height)
1737 {
1738 if(boxes.empty())
1739 {
1740 pos.x -= m_padding.left + m_borders.left;
1741 pos.width += m_padding.left + m_borders.left;
1742 }
1743 boxes.push_back(pos);
1744 }
1745 if(!boxes.empty())
1746 {
1747 if(m_padding.right + m_borders.right > 0)
1748 {
1749 boxes.back().width += m_padding.right + m_borders.right;
1750 }
1751 }
1752 }
1753
on_mouse_over()1754 bool litehtml::html_tag::on_mouse_over()
1755 {
1756 bool ret = false;
1757
1758 element::ptr el = shared_from_this();
1759 while(el)
1760 {
1761 if(el->set_pseudo_class(_t("hover"), true))
1762 {
1763 ret = true;
1764 }
1765 el = el->parent();
1766 }
1767
1768 return ret;
1769 }
1770
find_styles_changes(position::vector & redraw_boxes,int x,int y)1771 bool litehtml::html_tag::find_styles_changes( position::vector& redraw_boxes, int x, int y )
1772 {
1773 if(m_display == display_inline_text)
1774 {
1775 return false;
1776 }
1777
1778 bool ret = false;
1779 bool apply = false;
1780 for (used_selector::vector::iterator iter = m_used_styles.begin(); iter != m_used_styles.end() && !apply; iter++)
1781 {
1782 if((*iter)->m_selector->is_media_valid())
1783 {
1784 int res = select(*((*iter)->m_selector), true);
1785 if( (res == select_no_match && (*iter)->m_used) || (res == select_match && !(*iter)->m_used) )
1786 {
1787 apply = true;
1788 }
1789 }
1790 }
1791
1792 if(apply)
1793 {
1794 if(m_display == display_inline || m_display == display_table_row)
1795 {
1796 position::vector boxes;
1797 get_inline_boxes(boxes);
1798 for(position::vector::iterator pos = boxes.begin(); pos != boxes.end(); pos++)
1799 {
1800 pos->x += x;
1801 pos->y += y;
1802 redraw_boxes.push_back(*pos);
1803 }
1804 } else
1805 {
1806 position pos = m_pos;
1807 if(m_el_position != element_position_fixed)
1808 {
1809 pos.x += x;
1810 pos.y += y;
1811 }
1812 pos += m_padding;
1813 pos += m_borders;
1814 redraw_boxes.push_back(pos);
1815 }
1816
1817 ret = true;
1818 refresh_styles();
1819 parse_styles();
1820 }
1821 for (auto& el : m_children)
1822 {
1823 if(!el->skip())
1824 {
1825 if(m_el_position != element_position_fixed)
1826 {
1827 if(el->find_styles_changes(redraw_boxes, x + m_pos.x, y + m_pos.y))
1828 {
1829 ret = true;
1830 }
1831 } else
1832 {
1833 if(el->find_styles_changes(redraw_boxes, m_pos.x, m_pos.y))
1834 {
1835 ret = true;
1836 }
1837 }
1838 }
1839 }
1840 return ret;
1841 }
1842
on_mouse_leave()1843 bool litehtml::html_tag::on_mouse_leave()
1844 {
1845 bool ret = false;
1846
1847 element::ptr el = shared_from_this();
1848 while(el)
1849 {
1850 if(el->set_pseudo_class(_t("hover"), false))
1851 {
1852 ret = true;
1853 }
1854 if(el->set_pseudo_class(_t("active"), false))
1855 {
1856 ret = true;
1857 }
1858 el = el->parent();
1859 }
1860
1861 return ret;
1862 }
1863
on_lbutton_down()1864 bool litehtml::html_tag::on_lbutton_down()
1865 {
1866 bool ret = false;
1867
1868 element::ptr el = shared_from_this();
1869 while (el)
1870 {
1871 if (el->set_pseudo_class(_t("active"), true))
1872 {
1873 ret = true;
1874 }
1875 el = el->parent();
1876 }
1877
1878 return ret;
1879 }
1880
on_lbutton_up()1881 bool litehtml::html_tag::on_lbutton_up()
1882 {
1883 bool ret = false;
1884
1885 element::ptr el = shared_from_this();
1886 while (el)
1887 {
1888 if (el->set_pseudo_class(_t("active"), false))
1889 {
1890 ret = true;
1891 }
1892 el = el->parent();
1893 }
1894
1895 on_click();
1896
1897 return ret;
1898 }
1899
on_click()1900 void litehtml::html_tag::on_click()
1901 {
1902 if (have_parent())
1903 {
1904 element::ptr el_parent = parent();
1905 if (el_parent)
1906 {
1907 el_parent->on_click();
1908 }
1909 }
1910 }
1911
get_cursor()1912 const litehtml::tchar_t* litehtml::html_tag::get_cursor()
1913 {
1914 return get_style_property(_t("cursor"), true, 0);
1915 }
1916
1917 static const int font_size_table[8][7] =
1918 {
1919 { 9, 9, 9, 9, 11, 14, 18},
1920 { 9, 9, 9, 10, 12, 15, 20},
1921 { 9, 9, 9, 11, 13, 17, 22},
1922 { 9, 9, 10, 12, 14, 18, 24},
1923 { 9, 9, 10, 13, 16, 20, 26},
1924 { 9, 9, 11, 14, 17, 21, 28},
1925 { 9, 10, 12, 15, 17, 23, 30},
1926 { 9, 10, 13, 16, 18, 24, 32}
1927 };
1928
1929
init_font()1930 void litehtml::html_tag::init_font()
1931 {
1932 // initialize font size
1933 const tchar_t* str = get_style_property(_t("font-size"), false, 0);
1934
1935 int parent_sz = 0;
1936 int doc_font_size = get_document()->container()->get_default_font_size();
1937 element::ptr el_parent = parent();
1938 if (el_parent)
1939 {
1940 parent_sz = el_parent->get_font_size();
1941 } else
1942 {
1943 parent_sz = doc_font_size;
1944 }
1945
1946
1947 if(!str)
1948 {
1949 m_font_size = parent_sz;
1950 } else
1951 {
1952 m_font_size = parent_sz;
1953
1954 css_length sz;
1955 sz.fromString(str, font_size_strings);
1956 if(sz.is_predefined())
1957 {
1958 int idx_in_table = doc_font_size - 9;
1959 if(idx_in_table >= 0 && idx_in_table <= 7)
1960 {
1961 if(sz.predef() >= fontSize_xx_small && sz.predef() <= fontSize_xx_large)
1962 {
1963 m_font_size = font_size_table[idx_in_table][sz.predef()];
1964 } else
1965 {
1966 m_font_size = doc_font_size;
1967 }
1968 } else
1969 {
1970 switch(sz.predef())
1971 {
1972 case fontSize_xx_small:
1973 m_font_size = doc_font_size * 3 / 5;
1974 break;
1975 case fontSize_x_small:
1976 m_font_size = doc_font_size * 3 / 4;
1977 break;
1978 case fontSize_small:
1979 m_font_size = doc_font_size * 8 / 9;
1980 break;
1981 case fontSize_large:
1982 m_font_size = doc_font_size * 6 / 5;
1983 break;
1984 case fontSize_x_large:
1985 m_font_size = doc_font_size * 3 / 2;
1986 break;
1987 case fontSize_xx_large:
1988 m_font_size = doc_font_size * 2;
1989 break;
1990 default:
1991 m_font_size = doc_font_size;
1992 break;
1993 }
1994 }
1995 } else
1996 {
1997 if(sz.units() == css_units_percentage)
1998 {
1999 m_font_size = sz.calc_percent(parent_sz);
2000 } else if(sz.units() == css_units_none)
2001 {
2002 m_font_size = parent_sz;
2003 } else
2004 {
2005 m_font_size = get_document()->cvt_units(sz, parent_sz);
2006 }
2007 }
2008 }
2009
2010 // initialize font
2011 const tchar_t* name = get_style_property(_t("font-family"), true, _t("inherit"));
2012 const tchar_t* weight = get_style_property(_t("font-weight"), true, _t("normal"));
2013 const tchar_t* style = get_style_property(_t("font-style"), true, _t("normal"));
2014 const tchar_t* decoration = get_style_property(_t("text-decoration"), true, _t("none"));
2015
2016 m_font = get_document()->get_font(name, m_font_size, weight, style, decoration, &m_font_metrics);
2017 }
2018
is_break() const2019 bool litehtml::html_tag::is_break() const
2020 {
2021 return false;
2022 }
2023
set_tagName(const tchar_t * tag)2024 void litehtml::html_tag::set_tagName( const tchar_t* tag )
2025 {
2026 tstring s_val = tag;
2027 for(size_t i = 0; i < s_val.length(); i++)
2028 {
2029 s_val[i] = std::tolower(s_val[i], std::locale::classic());
2030 }
2031 m_tag = s_val;
2032 }
2033
draw_background(uint_ptr hdc,int x,int y,const position * clip)2034 void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const position* clip )
2035 {
2036 position pos = m_pos;
2037 pos.x += x;
2038 pos.y += y;
2039
2040 position el_pos = pos;
2041 el_pos += m_padding;
2042 el_pos += m_borders;
2043
2044 if(m_display != display_inline && m_display != display_table_row)
2045 {
2046 if(el_pos.does_intersect(clip))
2047 {
2048 const background* bg = get_background();
2049 if(bg)
2050 {
2051 background_paint bg_paint;
2052 init_background_paint(pos, bg_paint, bg);
2053
2054 get_document()->container()->draw_background(hdc, bg_paint);
2055 }
2056 position border_box = pos;
2057 border_box += m_padding;
2058 border_box += m_borders;
2059
2060 borders bdr = m_css_borders;
2061 bdr.radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
2062
2063 get_document()->container()->draw_borders(hdc, bdr, border_box, have_parent() ? false : true);
2064 }
2065 } else
2066 {
2067 const background* bg = get_background();
2068
2069 position::vector boxes;
2070 get_inline_boxes(boxes);
2071
2072 background_paint bg_paint;
2073 position content_box;
2074
2075 for(position::vector::iterator box = boxes.begin(); box != boxes.end(); box++)
2076 {
2077 box->x += x;
2078 box->y += y;
2079
2080 if(box->does_intersect(clip))
2081 {
2082 content_box = *box;
2083 content_box -= m_borders;
2084 content_box -= m_padding;
2085
2086 if(bg)
2087 {
2088 init_background_paint(content_box, bg_paint, bg);
2089 }
2090
2091 css_borders bdr;
2092
2093 // set left borders radius for the first box
2094 if(box == boxes.begin())
2095 {
2096 bdr.radius.bottom_left_x = m_css_borders.radius.bottom_left_x;
2097 bdr.radius.bottom_left_y = m_css_borders.radius.bottom_left_y;
2098 bdr.radius.top_left_x = m_css_borders.radius.top_left_x;
2099 bdr.radius.top_left_y = m_css_borders.radius.top_left_y;
2100 }
2101
2102 // set right borders radius for the last box
2103 if(box == boxes.end() - 1)
2104 {
2105 bdr.radius.bottom_right_x = m_css_borders.radius.bottom_right_x;
2106 bdr.radius.bottom_right_y = m_css_borders.radius.bottom_right_y;
2107 bdr.radius.top_right_x = m_css_borders.radius.top_right_x;
2108 bdr.radius.top_right_y = m_css_borders.radius.top_right_y;
2109 }
2110
2111
2112 bdr.top = m_css_borders.top;
2113 bdr.bottom = m_css_borders.bottom;
2114 if(box == boxes.begin())
2115 {
2116 bdr.left = m_css_borders.left;
2117 }
2118 if(box == boxes.end() - 1)
2119 {
2120 bdr.right = m_css_borders.right;
2121 }
2122
2123
2124 if(bg)
2125 {
2126 bg_paint.border_radius = bdr.radius.calc_percents(bg_paint.border_box.width, bg_paint.border_box.width);
2127 get_document()->container()->draw_background(hdc, bg_paint);
2128 }
2129 borders b = bdr;
2130 b.radius = bdr.radius.calc_percents(box->width, box->height);
2131 get_document()->container()->draw_borders(hdc, b, *box, false);
2132 }
2133 }
2134 }
2135 }
2136
render_inline(const element::ptr & container,int max_width)2137 int litehtml::html_tag::render_inline(const element::ptr &container, int max_width)
2138 {
2139 int ret_width = 0;
2140 int rw = 0;
2141
2142 white_space ws = get_white_space();
2143 bool skip_spaces = false;
2144 if (ws == white_space_normal ||
2145 ws == white_space_nowrap ||
2146 ws == white_space_pre_line)
2147 {
2148 skip_spaces = true;
2149 }
2150 bool was_space = false;
2151
2152 for (auto& el : m_children)
2153 {
2154 // skip spaces to make rendering a bit faster
2155 if (skip_spaces)
2156 {
2157 if (el->is_white_space())
2158 {
2159 if (was_space)
2160 {
2161 el->skip(true);
2162 continue;
2163 }
2164 else
2165 {
2166 was_space = true;
2167 }
2168 }
2169 else
2170 {
2171 was_space = false;
2172 }
2173 }
2174
2175 rw = container->place_element( el, max_width );
2176 if(rw > ret_width)
2177 {
2178 ret_width = rw;
2179 }
2180 }
2181 return ret_width;
2182 }
2183
place_element(const element::ptr & el,int max_width)2184 int litehtml::html_tag::place_element(const element::ptr &el, int max_width)
2185 {
2186 if(el->get_display() == display_none) return 0;
2187
2188 if(el->get_display() == display_inline)
2189 {
2190 return el->render_inline(shared_from_this(), max_width);
2191 }
2192
2193 element_position el_position = el->get_element_position();
2194
2195 if(el_position == element_position_absolute || el_position == element_position_fixed)
2196 {
2197 int line_top = 0;
2198 if(!m_boxes.empty())
2199 {
2200 if(m_boxes.back()->get_type() == box_line)
2201 {
2202 line_top = m_boxes.back()->top();
2203 if(!m_boxes.back()->is_empty())
2204 {
2205 line_top += line_height();
2206 }
2207 } else
2208 {
2209 line_top = m_boxes.back()->bottom();
2210 }
2211 }
2212
2213 el->render(0, line_top, max_width);
2214 el->m_pos.x += el->content_margins_left();
2215 el->m_pos.y += el->content_margins_top();
2216
2217 return 0;
2218 }
2219
2220 int ret_width = 0;
2221
2222 switch(el->get_float())
2223 {
2224 case float_left:
2225 {
2226 int line_top = 0;
2227 if(!m_boxes.empty())
2228 {
2229 if(m_boxes.back()->get_type() == box_line)
2230 {
2231 line_top = m_boxes.back()->top();
2232 } else
2233 {
2234 line_top = m_boxes.back()->bottom();
2235 }
2236 }
2237 line_top = get_cleared_top(el, line_top);
2238 int line_left = 0;
2239 int line_right = max_width;
2240 get_line_left_right(line_top, max_width, line_left, line_right);
2241
2242 el->render(line_left, line_top, line_right);
2243 if(el->right() > line_right)
2244 {
2245 int new_top = find_next_line_top(el->top(), el->width(), max_width);
2246 el->m_pos.x = get_line_left(new_top) + el->content_margins_left();
2247 el->m_pos.y = new_top + el->content_margins_top();
2248 }
2249 add_float(el, 0, 0);
2250 ret_width = fix_line_width(max_width, float_left);
2251 if(!ret_width)
2252 {
2253 ret_width = el->right();
2254 }
2255 }
2256 break;
2257 case float_right:
2258 {
2259 int line_top = 0;
2260 if(!m_boxes.empty())
2261 {
2262 if(m_boxes.back()->get_type() == box_line)
2263 {
2264 line_top = m_boxes.back()->top();
2265 } else
2266 {
2267 line_top = m_boxes.back()->bottom();
2268 }
2269 }
2270 line_top = get_cleared_top(el, line_top);
2271 int line_left = 0;
2272 int line_right = max_width;
2273 get_line_left_right(line_top, max_width, line_left, line_right);
2274
2275 el->render(0, line_top, line_right);
2276
2277 if(line_left + el->width() > line_right)
2278 {
2279 int new_top = find_next_line_top(el->top(), el->width(), max_width);
2280 el->m_pos.x = get_line_right(new_top, max_width) - el->width() + el->content_margins_left();
2281 el->m_pos.y = new_top + el->content_margins_top();
2282 } else
2283 {
2284 el->m_pos.x = line_right - el->width() + el->content_margins_left();
2285 }
2286 add_float(el, 0, 0);
2287 ret_width = fix_line_width(max_width, float_right);
2288
2289 if(!ret_width)
2290 {
2291 line_left = 0;
2292 line_right = max_width;
2293 get_line_left_right(line_top, max_width, line_left, line_right);
2294
2295 ret_width = ret_width + (max_width - line_right);
2296 }
2297 }
2298 break;
2299 default:
2300 {
2301 line_context line_ctx;
2302 line_ctx.top = 0;
2303 if (!m_boxes.empty())
2304 {
2305 line_ctx.top = m_boxes.back()->top();
2306 }
2307 line_ctx.left = 0;
2308 line_ctx.right = max_width;
2309 line_ctx.fix_top();
2310 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2311
2312 switch(el->get_display())
2313 {
2314 case display_inline_block:
2315 case display_inline_table:
2316 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.right);
2317 break;
2318 case display_block:
2319 if(el->is_replaced() || el->is_floats_holder())
2320 {
2321 element::ptr el_parent = el->parent();
2322 el->m_pos.width = el->get_css_width().calc_percent(line_ctx.right - line_ctx.left);
2323 el->m_pos.height = el->get_css_height().calc_percent(el_parent ? el_parent->m_pos.height : 0);
2324 }
2325 el->calc_outlines(line_ctx.right - line_ctx.left);
2326 break;
2327 case display_inline_text:
2328 {
2329 litehtml::size sz;
2330 el->get_content_size(sz, line_ctx.right);
2331 el->m_pos = sz;
2332 }
2333 break;
2334 default:
2335 ret_width = 0;
2336 break;
2337 }
2338
2339 bool add_box = true;
2340 if(!m_boxes.empty())
2341 {
2342 if(m_boxes.back()->can_hold(el, m_white_space))
2343 {
2344 add_box = false;
2345 }
2346 }
2347 if(add_box)
2348 {
2349 new_box(el, max_width, line_ctx);
2350 } else if(!m_boxes.empty())
2351 {
2352 line_ctx.top = m_boxes.back()->top();
2353 }
2354
2355 if (line_ctx.top != line_ctx.calculatedTop)
2356 {
2357 line_ctx.left = 0;
2358 line_ctx.right = max_width;
2359 line_ctx.fix_top();
2360 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2361 }
2362
2363 if(!el->is_inline_box())
2364 {
2365 if(m_boxes.size() == 1)
2366 {
2367 if(collapse_top_margin())
2368 {
2369 int shift = el->margin_top();
2370 if(shift >= 0)
2371 {
2372 line_ctx.top -= shift;
2373 m_boxes.back()->y_shift(-shift);
2374 }
2375 }
2376 } else
2377 {
2378 int shift = 0;
2379 int prev_margin = m_boxes[m_boxes.size() - 2]->bottom_margin();
2380
2381 if(prev_margin > el->margin_top())
2382 {
2383 shift = el->margin_top();
2384 } else
2385 {
2386 shift = prev_margin;
2387 }
2388 if(shift >= 0)
2389 {
2390 line_ctx.top -= shift;
2391 m_boxes.back()->y_shift(-shift);
2392 }
2393 }
2394 }
2395
2396 switch(el->get_display())
2397 {
2398 case display_table:
2399 case display_list_item:
2400 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width());
2401 break;
2402 case display_block:
2403 case display_table_cell:
2404 case display_table_caption:
2405 case display_table_row:
2406 if(el->is_replaced() || el->is_floats_holder())
2407 {
2408 ret_width = el->render(line_ctx.left, line_ctx.top, line_ctx.width()) + line_ctx.left + (max_width - line_ctx.right);
2409 } else
2410 {
2411 ret_width = el->render(0, line_ctx.top, max_width);
2412 }
2413 break;
2414 default:
2415 ret_width = 0;
2416 break;
2417 }
2418
2419 m_boxes.back()->add_element(el);
2420
2421 if(el->is_inline_box() && !el->skip())
2422 {
2423 ret_width = el->right() + (max_width - line_ctx.right);
2424 }
2425 }
2426 break;
2427 }
2428
2429 return ret_width;
2430 }
2431
set_pseudo_class(const tchar_t * pclass,bool add)2432 bool litehtml::html_tag::set_pseudo_class( const tchar_t* pclass, bool add )
2433 {
2434 bool ret = false;
2435 if(add)
2436 {
2437 if(std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass) == m_pseudo_classes.end())
2438 {
2439 m_pseudo_classes.push_back(pclass);
2440 ret = true;
2441 }
2442 } else
2443 {
2444 string_vector::iterator pi = std::find(m_pseudo_classes.begin(), m_pseudo_classes.end(), pclass);
2445 if(pi != m_pseudo_classes.end())
2446 {
2447 m_pseudo_classes.erase(pi);
2448 ret = true;
2449 }
2450 }
2451 return ret;
2452 }
2453
set_class(const tchar_t * pclass,bool add)2454 bool litehtml::html_tag::set_class( const tchar_t* pclass, bool add )
2455 {
2456 string_vector classes;
2457 bool changed = false;
2458
2459 split_string( pclass, classes, _t(" ") );
2460
2461 if(add)
2462 {
2463 for( auto & _class : classes )
2464 {
2465 if(std::find(m_class_values.begin(), m_class_values.end(), _class) == m_class_values.end())
2466 {
2467 m_class_values.push_back( std::move( _class ) );
2468 changed = true;
2469 }
2470 }
2471 } else
2472 {
2473 for( const auto & _class : classes )
2474 {
2475 auto end = std::remove(m_class_values.begin(), m_class_values.end(), _class);
2476
2477 if(end != m_class_values.end())
2478 {
2479 m_class_values.erase(end, m_class_values.end());
2480 changed = true;
2481 }
2482 }
2483 }
2484
2485 if( changed )
2486 {
2487 tstring class_string;
2488 join_string(class_string, m_class_values, _t(" "));
2489 set_attr(_t("class"), class_string.c_str());
2490
2491 return true;
2492 }
2493 else
2494 {
2495 return false;
2496 }
2497
2498 }
2499
line_height() const2500 int litehtml::html_tag::line_height() const
2501 {
2502 return m_line_height;
2503 }
2504
is_replaced() const2505 bool litehtml::html_tag::is_replaced() const
2506 {
2507 return false;
2508 }
2509
finish_last_box(bool end_of_render)2510 int litehtml::html_tag::finish_last_box(bool end_of_render)
2511 {
2512 int line_top = 0;
2513
2514 if(!m_boxes.empty())
2515 {
2516 m_boxes.back()->finish(end_of_render);
2517
2518 if(m_boxes.back()->is_empty())
2519 {
2520 line_top = m_boxes.back()->top();
2521 m_boxes.pop_back();
2522 }
2523
2524 if(!m_boxes.empty())
2525 {
2526 line_top = m_boxes.back()->bottom();
2527 }
2528 }
2529 return line_top;
2530 }
2531
new_box(const element::ptr & el,int max_width,line_context & line_ctx)2532 int litehtml::html_tag::new_box(const element::ptr &el, int max_width, line_context& line_ctx)
2533 {
2534 line_ctx.top = get_cleared_top(el, finish_last_box());
2535
2536 line_ctx.left = 0;
2537 line_ctx.right = max_width;
2538 line_ctx.fix_top();
2539 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2540
2541 if(el->is_inline_box() || el->is_floats_holder())
2542 {
2543 if (el->width() > line_ctx.right - line_ctx.left)
2544 {
2545 line_ctx.top = find_next_line_top(line_ctx.top, el->width(), max_width);
2546 line_ctx.left = 0;
2547 line_ctx.right = max_width;
2548 line_ctx.fix_top();
2549 get_line_left_right(line_ctx.top, max_width, line_ctx.left, line_ctx.right);
2550 }
2551 }
2552
2553 int first_line_margin = 0;
2554 if(m_boxes.empty() && m_list_style_type != list_style_type_none && m_list_style_position == list_style_position_inside)
2555 {
2556 int sz_font = get_font_size();
2557 first_line_margin = sz_font;
2558 }
2559
2560 if(el->is_inline_box())
2561 {
2562 int text_indent = 0;
2563 if(m_css_text_indent.val() != 0)
2564 {
2565 bool line_box_found = false;
2566 for(box::vector::iterator iter = m_boxes.begin(); iter != m_boxes.end(); iter++)
2567 {
2568 if((*iter)->get_type() == box_line)
2569 {
2570 line_box_found = true;
2571 break;
2572 }
2573 }
2574 if(!line_box_found)
2575 {
2576 text_indent = m_css_text_indent.calc_percent(max_width);
2577 }
2578 }
2579
2580 font_metrics fm;
2581 get_font(&fm);
2582 m_boxes.emplace_back(std::unique_ptr<line_box>(new line_box(line_ctx.top, line_ctx.left + first_line_margin + text_indent, line_ctx.right, line_height(), fm, m_text_align)));
2583 } else
2584 {
2585 m_boxes.emplace_back(std::unique_ptr<block_box>(new block_box(line_ctx.top, line_ctx.left, line_ctx.right)));
2586 }
2587
2588 return line_ctx.top;
2589 }
2590
get_cleared_top(const element::ptr & el,int line_top) const2591 int litehtml::html_tag::get_cleared_top(const element::ptr &el, int line_top) const
2592 {
2593 switch(el->get_clear())
2594 {
2595 case clear_left:
2596 {
2597 int fh = get_left_floats_height();
2598 if(fh && fh > line_top)
2599 {
2600 line_top = fh;
2601 }
2602 }
2603 break;
2604 case clear_right:
2605 {
2606 int fh = get_right_floats_height();
2607 if(fh && fh > line_top)
2608 {
2609 line_top = fh;
2610 }
2611 }
2612 break;
2613 case clear_both:
2614 {
2615 int fh = get_floats_height();
2616 if(fh && fh > line_top)
2617 {
2618 line_top = fh;
2619 }
2620 }
2621 break;
2622 default:
2623 if(el->get_float() != float_none)
2624 {
2625 int fh = get_floats_height(el->get_float());
2626 if(fh && fh > line_top)
2627 {
2628 line_top = fh;
2629 }
2630 }
2631 break;
2632 }
2633 return line_top;
2634 }
2635
get_display() const2636 litehtml::style_display litehtml::html_tag::get_display() const
2637 {
2638 return m_display;
2639 }
2640
get_float() const2641 litehtml::element_float litehtml::html_tag::get_float() const
2642 {
2643 return m_float;
2644 }
2645
is_floats_holder() const2646 bool litehtml::html_tag::is_floats_holder() const
2647 {
2648 if( m_display == display_inline_block ||
2649 m_display == display_table_cell ||
2650 !have_parent() ||
2651 is_body() ||
2652 m_float != float_none ||
2653 m_el_position == element_position_absolute ||
2654 m_el_position == element_position_fixed ||
2655 m_overflow > overflow_visible)
2656 {
2657 return true;
2658 }
2659 return false;
2660 }
2661
is_first_child_inline(const element::ptr & el) const2662 bool litehtml::html_tag::is_first_child_inline(const element::ptr& el) const
2663 {
2664 if(!m_children.empty())
2665 {
2666 for (const auto& this_el : m_children)
2667 {
2668 if (!this_el->is_white_space())
2669 {
2670 if (el == this_el)
2671 {
2672 return true;
2673 }
2674 if (this_el->get_display() == display_inline)
2675 {
2676 if (this_el->have_inline_child())
2677 {
2678 return false;
2679 }
2680 } else
2681 {
2682 return false;
2683 }
2684 }
2685 }
2686 }
2687 return false;
2688 }
2689
is_last_child_inline(const element::ptr & el)2690 bool litehtml::html_tag::is_last_child_inline(const element::ptr& el)
2691 {
2692 if(!m_children.empty())
2693 {
2694 for (auto this_el = m_children.rbegin(); this_el < m_children.rend(); ++this_el)
2695 {
2696 if (!(*this_el)->is_white_space())
2697 {
2698 if (el == (*this_el))
2699 {
2700 return true;
2701 }
2702 if ((*this_el)->get_display() == display_inline)
2703 {
2704 if ((*this_el)->have_inline_child())
2705 {
2706 return false;
2707 }
2708 } else
2709 {
2710 return false;
2711 }
2712 }
2713 }
2714 }
2715 return false;
2716 }
2717
get_white_space() const2718 litehtml::white_space litehtml::html_tag::get_white_space() const
2719 {
2720 return m_white_space;
2721 }
2722
get_vertical_align() const2723 litehtml::vertical_align litehtml::html_tag::get_vertical_align() const
2724 {
2725 return m_vertical_align;
2726 }
2727
get_css_left() const2728 litehtml::css_length litehtml::html_tag::get_css_left() const
2729 {
2730 return m_css_offsets.left;
2731 }
2732
get_css_right() const2733 litehtml::css_length litehtml::html_tag::get_css_right() const
2734 {
2735 return m_css_offsets.right;
2736 }
2737
get_css_top() const2738 litehtml::css_length litehtml::html_tag::get_css_top() const
2739 {
2740 return m_css_offsets.top;
2741 }
2742
get_css_bottom() const2743 litehtml::css_length litehtml::html_tag::get_css_bottom() const
2744 {
2745 return m_css_offsets.bottom;
2746 }
2747
2748
get_css_offsets() const2749 litehtml::css_offsets litehtml::html_tag::get_css_offsets() const
2750 {
2751 return m_css_offsets;
2752 }
2753
get_clear() const2754 litehtml::element_clear litehtml::html_tag::get_clear() const
2755 {
2756 return m_clear;
2757 }
2758
get_css_width() const2759 litehtml::css_length litehtml::html_tag::get_css_width() const
2760 {
2761 return m_css_width;
2762 }
2763
get_css_height() const2764 litehtml::css_length litehtml::html_tag::get_css_height() const
2765 {
2766 return m_css_height;
2767 }
2768
get_children_count() const2769 size_t litehtml::html_tag::get_children_count() const
2770 {
2771 return m_children.size();
2772 }
2773
get_child(int idx) const2774 litehtml::element::ptr litehtml::html_tag::get_child( int idx ) const
2775 {
2776 return m_children[idx];
2777 }
2778
set_css_width(css_length & w)2779 void litehtml::html_tag::set_css_width( css_length& w )
2780 {
2781 m_css_width = w;
2782 }
2783
apply_vertical_align()2784 void litehtml::html_tag::apply_vertical_align()
2785 {
2786 if(!m_boxes.empty())
2787 {
2788 int add = 0;
2789 int content_height = m_boxes.back()->bottom();
2790
2791 if(m_pos.height > content_height)
2792 {
2793 switch(m_vertical_align)
2794 {
2795 case va_middle:
2796 add = (m_pos.height - content_height) / 2;
2797 break;
2798 case va_bottom:
2799 add = m_pos.height - content_height;
2800 break;
2801 default:
2802 add = 0;
2803 break;
2804 }
2805 }
2806
2807 if(add)
2808 {
2809 for(size_t i = 0; i < m_boxes.size(); i++)
2810 {
2811 m_boxes[i]->y_shift(add);
2812 }
2813 }
2814 }
2815 }
2816
get_element_position(css_offsets * offsets) const2817 litehtml::element_position litehtml::html_tag::get_element_position(css_offsets* offsets) const
2818 {
2819 if(offsets && m_el_position != element_position_static)
2820 {
2821 *offsets = m_css_offsets;
2822 }
2823 return m_el_position;
2824 }
2825
init_background_paint(position pos,background_paint & bg_paint,const background * bg)2826 void litehtml::html_tag::init_background_paint(position pos, background_paint &bg_paint, const background* bg)
2827 {
2828 if(!bg) return;
2829
2830 bg_paint = *bg;
2831 position content_box = pos;
2832 position padding_box = pos;
2833 padding_box += m_padding;
2834 position border_box = padding_box;
2835 border_box += m_borders;
2836
2837 switch(bg->m_clip)
2838 {
2839 case litehtml::background_box_padding:
2840 bg_paint.clip_box = padding_box;
2841 break;
2842 case litehtml::background_box_content:
2843 bg_paint.clip_box = content_box;
2844 break;
2845 default:
2846 bg_paint.clip_box = border_box;
2847 break;
2848 }
2849
2850 switch(bg->m_origin)
2851 {
2852 case litehtml::background_box_border:
2853 bg_paint.origin_box = border_box;
2854 break;
2855 case litehtml::background_box_content:
2856 bg_paint.origin_box = content_box;
2857 break;
2858 default:
2859 bg_paint.origin_box = padding_box;
2860 break;
2861 }
2862
2863 if(!bg_paint.image.empty())
2864 {
2865 get_document()->container()->get_image_size(bg_paint.image.c_str(), bg_paint.baseurl.c_str(), bg_paint.image_size);
2866 if(bg_paint.image_size.width && bg_paint.image_size.height)
2867 {
2868 litehtml::size img_new_sz = bg_paint.image_size;
2869 double img_ar_width = (double) bg_paint.image_size.width / (double) bg_paint.image_size.height;
2870 double img_ar_height = (double) bg_paint.image_size.height / (double) bg_paint.image_size.width;
2871
2872
2873 if(bg->m_position.width.is_predefined())
2874 {
2875 switch(bg->m_position.width.predef())
2876 {
2877 case litehtml::background_size_contain:
2878 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) <= bg_paint.origin_box.height )
2879 {
2880 img_new_sz.width = bg_paint.origin_box.width;
2881 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
2882 } else
2883 {
2884 img_new_sz.height = bg_paint.origin_box.height;
2885 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
2886 }
2887 break;
2888 case litehtml::background_size_cover:
2889 if( (int) ((double) bg_paint.origin_box.width * img_ar_height) >= bg_paint.origin_box.height )
2890 {
2891 img_new_sz.width = bg_paint.origin_box.width;
2892 img_new_sz.height = (int) ((double) bg_paint.origin_box.width * img_ar_height);
2893 } else
2894 {
2895 img_new_sz.height = bg_paint.origin_box.height;
2896 img_new_sz.width = (int) ((double) bg_paint.origin_box.height * img_ar_width);
2897 }
2898 break;
2899 break;
2900 case litehtml::background_size_auto:
2901 if(!bg->m_position.height.is_predefined())
2902 {
2903 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2904 img_new_sz.width = (int) ((double) img_new_sz.height * img_ar_width);
2905 }
2906 break;
2907 }
2908 } else
2909 {
2910 img_new_sz.width = bg->m_position.width.calc_percent(bg_paint.origin_box.width);
2911 if(bg->m_position.height.is_predefined())
2912 {
2913 img_new_sz.height = (int) ((double) img_new_sz.width * img_ar_height);
2914 } else
2915 {
2916 img_new_sz.height = bg->m_position.height.calc_percent(bg_paint.origin_box.height);
2917 }
2918 }
2919
2920 bg_paint.image_size = img_new_sz;
2921 bg_paint.position_x = bg_paint.origin_box.x + (int) bg->m_position.x.calc_percent(bg_paint.origin_box.width - bg_paint.image_size.width);
2922 bg_paint.position_y = bg_paint.origin_box.y + (int) bg->m_position.y.calc_percent(bg_paint.origin_box.height - bg_paint.image_size.height);
2923 }
2924
2925 }
2926 bg_paint.border_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);;
2927 bg_paint.border_box = border_box;
2928 bg_paint.is_root = have_parent() ? false : true;
2929 }
2930
get_visibility() const2931 litehtml::visibility litehtml::html_tag::get_visibility() const
2932 {
2933 return m_visibility;
2934 }
2935
draw_list_marker(uint_ptr hdc,const position & pos)2936 void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position &pos )
2937 {
2938 list_marker lm;
2939
2940 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
2941 size img_size;
2942 if(list_image)
2943 {
2944 css::parse_css_url(list_image, lm.image);
2945 lm.baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
2946 get_document()->container()->get_image_size(lm.image.c_str(), lm.baseurl, img_size);
2947 } else
2948 {
2949 lm.baseurl = 0;
2950 }
2951
2952 int ln_height = line_height();
2953 int sz_font = get_font_size();
2954 lm.pos.x = pos.x;
2955 lm.pos.width = sz_font - sz_font * 2 / 3;
2956 lm.color = get_color(_t("color"), true, web_color(0, 0, 0));
2957 lm.marker_type = m_list_style_type;
2958 lm.font = get_font();
2959
2960 if (m_list_style_type >= list_style_type_armenian)
2961 {
2962 lm.pos.y = pos.y;
2963 lm.pos.height = pos.height;
2964 lm.index = get_attr(_t("list_index"), _t(""))[0];
2965 }
2966 else
2967 {
2968 lm.pos.height = sz_font - sz_font * 2 / 3;
2969 lm.pos.y = pos.y + ln_height / 2 - lm.pos.height / 2;
2970 lm.index = -1;
2971 }
2972
2973 if(img_size.width && img_size.height)
2974 {
2975 if(lm.pos.y + img_size.height > pos.y + pos.height)
2976 {
2977 lm.pos.y = pos.y + pos.height - img_size.height;
2978 }
2979 if(img_size.width > lm.pos.width)
2980 {
2981 lm.pos.x -= img_size.width - lm.pos.width;
2982 }
2983
2984 lm.pos.width = img_size.width;
2985 lm.pos.height = img_size.height;
2986 }
2987
2988 if (m_list_style_position == list_style_position_outside)
2989 {
2990 if (m_list_style_type >= list_style_type_armenian)
2991 {
2992 auto tw_space = get_document()->container()->text_width(_t(" "), lm.font);
2993 lm.pos.x = pos.x - tw_space * 2;
2994 lm.pos.width = tw_space;
2995 }
2996 else
2997 {
2998 lm.pos.x -= sz_font;
2999 }
3000 }
3001
3002 if (m_list_style_type >= list_style_type_armenian)
3003 {
3004 auto marker_text = get_list_marker_text(lm.index);
3005 lm.pos.height = ln_height;
3006 if (marker_text.empty())
3007 {
3008 get_document()->container()->draw_list_marker(hdc, lm);
3009 }
3010 else
3011 {
3012 marker_text += _t(".");
3013 auto tw = get_document()->container()->text_width(marker_text.c_str(), lm.font);
3014 auto text_pos = lm.pos;
3015 text_pos.move_to(text_pos.right() - tw, text_pos.y);
3016 text_pos.width = tw;
3017 get_document()->container()->draw_text(hdc, marker_text.c_str(), lm.font, lm.color, text_pos);
3018 }
3019 }
3020 else
3021 {
3022 get_document()->container()->draw_list_marker(hdc, lm);
3023 }
3024 }
3025
get_list_marker_text(int index)3026 litehtml::tstring litehtml::html_tag::get_list_marker_text(int index)
3027 {
3028 switch (m_list_style_type)
3029 {
3030 case litehtml::list_style_type_decimal:
3031 return t_to_string(index);
3032 case litehtml::list_style_type_decimal_leading_zero:
3033 {
3034 auto txt = t_to_string(index);
3035 if (txt.length() == 1)
3036 {
3037 txt = _t("0") + txt;
3038 }
3039 return txt;
3040 }
3041 case litehtml::list_style_type_lower_latin:
3042 case litehtml::list_style_type_lower_alpha:
3043 return num_cvt::to_latin_lower(index);
3044 case litehtml::list_style_type_lower_greek:
3045 return num_cvt::to_greek_lower(index);
3046 case litehtml::list_style_type_upper_alpha:
3047 case litehtml::list_style_type_upper_latin:
3048 return num_cvt::to_latin_upper(index);
3049 case litehtml::list_style_type_lower_roman:
3050 return num_cvt::to_roman_lower(index);
3051 case litehtml::list_style_type_upper_roman:
3052 return num_cvt::to_roman_upper(index);
3053 case litehtml::list_style_type_armenian:
3054 break;
3055 case litehtml::list_style_type_georgian:
3056 break;
3057 case litehtml::list_style_type_hebrew:
3058 break;
3059 case litehtml::list_style_type_hiragana:
3060 break;
3061 case litehtml::list_style_type_hiragana_iroha:
3062 break;
3063 case litehtml::list_style_type_katakana:
3064 break;
3065 case litehtml::list_style_type_katakana_iroha:
3066 break;
3067 }
3068 return _t("");
3069 }
3070
draw_children(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)3071 void litehtml::html_tag::draw_children( uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex )
3072 {
3073 if (m_display == display_table || m_display == display_inline_table)
3074 {
3075 draw_children_table(hdc, x, y, clip, flag, zindex);
3076 }
3077 else
3078 {
3079 draw_children_box(hdc, x, y, clip, flag, zindex);
3080 }
3081 }
3082
fetch_positioned()3083 bool litehtml::html_tag::fetch_positioned()
3084 {
3085 bool ret = false;
3086
3087 m_positioned.clear();
3088
3089 litehtml::element_position el_pos;
3090
3091 for(auto& el : m_children)
3092 {
3093 el_pos = el->get_element_position();
3094 if (el_pos != element_position_static)
3095 {
3096 add_positioned(el);
3097 }
3098 if (!ret && (el_pos == element_position_absolute || el_pos == element_position_fixed))
3099 {
3100 ret = true;
3101 }
3102 if(el->fetch_positioned())
3103 {
3104 ret = true;
3105 }
3106 }
3107 return ret;
3108 }
3109
get_zindex() const3110 int litehtml::html_tag::get_zindex() const
3111 {
3112 return m_z_index;
3113 }
3114
render_positioned(render_type rt)3115 void litehtml::html_tag::render_positioned(render_type rt)
3116 {
3117 position wnd_position;
3118 get_document()->container()->get_client_rect(wnd_position);
3119
3120 element_position el_position;
3121 bool process;
3122 for (auto& el : m_positioned)
3123 {
3124 el_position = el->get_element_position();
3125
3126 process = false;
3127 if(el->get_display() != display_none)
3128 {
3129 if(el_position == element_position_absolute)
3130 {
3131 if(rt != render_fixed_only)
3132 {
3133 process = true;
3134 }
3135 } else if(el_position == element_position_fixed)
3136 {
3137 if(rt != render_no_fixed)
3138 {
3139 process = true;
3140 }
3141 }
3142 }
3143
3144 if(process)
3145 {
3146 int parent_height = 0;
3147 int parent_width = 0;
3148 int client_x = 0;
3149 int client_y = 0;
3150 if(el_position == element_position_fixed)
3151 {
3152 parent_height = wnd_position.height;
3153 parent_width = wnd_position.width;
3154 client_x = wnd_position.left();
3155 client_y = wnd_position.top();
3156 } else
3157 {
3158 element::ptr el_parent = el->parent();
3159 if(el_parent)
3160 {
3161 parent_height = el_parent->height();
3162 parent_width = el_parent->width();
3163 }
3164 }
3165
3166 css_length css_left = el->get_css_left();
3167 css_length css_right = el->get_css_right();
3168 css_length css_top = el->get_css_top();
3169 css_length css_bottom = el->get_css_bottom();
3170
3171 bool need_render = false;
3172
3173 css_length el_w = el->get_css_width();
3174 css_length el_h = el->get_css_height();
3175
3176 int new_width = -1;
3177 int new_height = -1;
3178 if(el_w.units() == css_units_percentage && parent_width)
3179 {
3180 new_width = el_w.calc_percent(parent_width);
3181 if(el->m_pos.width != new_width)
3182 {
3183 need_render = true;
3184 el->m_pos.width = new_width;
3185 }
3186 }
3187
3188 if(el_h.units() == css_units_percentage && parent_height)
3189 {
3190 new_height = el_h.calc_percent(parent_height);
3191 if(el->m_pos.height != new_height)
3192 {
3193 need_render = true;
3194 el->m_pos.height = new_height;
3195 }
3196 }
3197
3198 bool cvt_x = false;
3199 bool cvt_y = false;
3200
3201 if(el_position == element_position_fixed)
3202 {
3203 if(!css_left.is_predefined() || !css_right.is_predefined())
3204 {
3205 if(!css_left.is_predefined() && css_right.is_predefined())
3206 {
3207 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
3208 } else if(css_left.is_predefined() && !css_right.is_predefined())
3209 {
3210 el->m_pos.x = parent_width - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3211 } else
3212 {
3213 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left();
3214 el->m_pos.width = parent_width - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3215 need_render = true;
3216 }
3217 }
3218
3219 if(!css_top.is_predefined() || !css_bottom.is_predefined())
3220 {
3221 if(!css_top.is_predefined() && css_bottom.is_predefined())
3222 {
3223 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
3224 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
3225 {
3226 el->m_pos.y = parent_height - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3227 } else
3228 {
3229 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top();
3230 el->m_pos.height = parent_height - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3231 need_render = true;
3232 }
3233 }
3234 } else
3235 {
3236 if(!css_left.is_predefined() || !css_right.is_predefined())
3237 {
3238 if(!css_left.is_predefined() && css_right.is_predefined())
3239 {
3240 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3241 } else if(css_left.is_predefined() && !css_right.is_predefined())
3242 {
3243 el->m_pos.x = m_pos.width + m_padding.right - css_right.calc_percent(parent_width) - el->m_pos.width - el->content_margins_right();
3244 } else
3245 {
3246 el->m_pos.x = css_left.calc_percent(parent_width) + el->content_margins_left() - m_padding.left;
3247 el->m_pos.width = m_pos.width + m_padding.left + m_padding.right - css_left.calc_percent(parent_width) - css_right.calc_percent(parent_width) - (el->content_margins_left() + el->content_margins_right());
3248 if (new_width != -1)
3249 {
3250 el->m_pos.x += (el->m_pos.width - new_width) / 2;
3251 el->m_pos.width = new_width;
3252 }
3253 need_render = true;
3254 }
3255 cvt_x = true;
3256 }
3257
3258 if(!css_top.is_predefined() || !css_bottom.is_predefined())
3259 {
3260 if(!css_top.is_predefined() && css_bottom.is_predefined())
3261 {
3262 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3263 } else if(css_top.is_predefined() && !css_bottom.is_predefined())
3264 {
3265 el->m_pos.y = m_pos.height + m_padding.bottom - css_bottom.calc_percent(parent_height) - el->m_pos.height - el->content_margins_bottom();
3266 } else
3267 {
3268 el->m_pos.y = css_top.calc_percent(parent_height) + el->content_margins_top() - m_padding.top;
3269 el->m_pos.height = m_pos.height + m_padding.top + m_padding.bottom - css_top.calc_percent(parent_height) - css_bottom.calc_percent(parent_height) - (el->content_margins_top() + el->content_margins_bottom());
3270 if (new_height != -1)
3271 {
3272 el->m_pos.y += (el->m_pos.height - new_height) / 2;
3273 el->m_pos.height = new_height;
3274 }
3275 need_render = true;
3276 }
3277 cvt_y = true;
3278 }
3279 }
3280
3281 if(cvt_x || cvt_y)
3282 {
3283 int offset_x = 0;
3284 int offset_y = 0;
3285 element::ptr cur_el = el->parent();
3286 element::ptr this_el = shared_from_this();
3287 while(cur_el && cur_el != this_el)
3288 {
3289 offset_x += cur_el->m_pos.x;
3290 offset_y += cur_el->m_pos.y;
3291 cur_el = cur_el->parent();
3292 }
3293 if(cvt_x) el->m_pos.x -= offset_x;
3294 if(cvt_y) el->m_pos.y -= offset_y;
3295 }
3296
3297 if(need_render)
3298 {
3299 position pos = el->m_pos;
3300 el->render(el->left(), el->top(), el->width(), true);
3301 el->m_pos = pos;
3302 }
3303
3304 if(el_position == element_position_fixed)
3305 {
3306 position fixed_pos;
3307 el->get_redraw_box(fixed_pos);
3308 get_document()->add_fixed_box(fixed_pos);
3309 }
3310 }
3311
3312 el->render_positioned();
3313 }
3314
3315 if(!m_positioned.empty())
3316 {
3317 std::stable_sort(m_positioned.begin(), m_positioned.end(), [](const litehtml::element::ptr& _Left, const litehtml::element::ptr& _Right)
3318 {
3319 return (_Left->get_zindex() < _Right->get_zindex());
3320 });
3321 }
3322 }
3323
draw_stacking_context(uint_ptr hdc,int x,int y,const position * clip,bool with_positioned)3324 void litehtml::html_tag::draw_stacking_context( uint_ptr hdc, int x, int y, const position* clip, bool with_positioned )
3325 {
3326 if(!is_visible()) return;
3327
3328 std::map<int, bool> zindexes;
3329 if(with_positioned)
3330 {
3331 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3332 {
3333 zindexes[(*i)->get_zindex()];
3334 }
3335
3336 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3337 {
3338 if(idx->first < 0)
3339 {
3340 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3341 }
3342 }
3343 }
3344 draw_children(hdc, x, y, clip, draw_block, 0);
3345 draw_children(hdc, x, y, clip, draw_floats, 0);
3346 draw_children(hdc, x, y, clip, draw_inlines, 0);
3347 if(with_positioned)
3348 {
3349 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3350 {
3351 if(idx->first == 0)
3352 {
3353 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3354 }
3355 }
3356
3357 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end(); idx++)
3358 {
3359 if(idx->first > 0)
3360 {
3361 draw_children(hdc, x, y, clip, draw_positioned, idx->first);
3362 }
3363 }
3364 }
3365 }
3366
get_overflow() const3367 litehtml::overflow litehtml::html_tag::get_overflow() const
3368 {
3369 return m_overflow;
3370 }
3371
is_nth_child(const element::ptr & el,int num,int off,bool of_type) const3372 bool litehtml::html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_type) const
3373 {
3374 int idx = 1;
3375 for(const auto& child : m_children)
3376 {
3377 if(child->get_display() != display_inline_text)
3378 {
3379 if( (!of_type) || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3380 {
3381 if(el == child)
3382 {
3383 if(num != 0)
3384 {
3385 if((idx - off) >= 0 && (idx - off) % num == 0)
3386 {
3387 return true;
3388 }
3389
3390 } else if(idx == off)
3391 {
3392 return true;
3393 }
3394 return false;
3395 }
3396 idx++;
3397 }
3398 if(el == child) break;
3399 }
3400 }
3401 return false;
3402 }
3403
is_nth_last_child(const element::ptr & el,int num,int off,bool of_type) const3404 bool litehtml::html_tag::is_nth_last_child(const element::ptr& el, int num, int off, bool of_type) const
3405 {
3406 int idx = 1;
3407 for(elements_vector::const_reverse_iterator child = m_children.rbegin(); child != m_children.rend(); child++)
3408 {
3409 if((*child)->get_display() != display_inline_text)
3410 {
3411 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), (*child)->get_tagName())) )
3412 {
3413 if(el == (*child))
3414 {
3415 if(num != 0)
3416 {
3417 if((idx - off) >= 0 && (idx - off) % num == 0)
3418 {
3419 return true;
3420 }
3421
3422 } else if(idx == off)
3423 {
3424 return true;
3425 }
3426 return false;
3427 }
3428 idx++;
3429 }
3430 if(el == (*child)) break;
3431 }
3432 }
3433 return false;
3434 }
3435
parse_nth_child_params(tstring param,int & num,int & off)3436 void litehtml::html_tag::parse_nth_child_params( tstring param, int &num, int &off )
3437 {
3438 if(param == _t("odd"))
3439 {
3440 num = 2;
3441 off = 1;
3442 } else if(param == _t("even"))
3443 {
3444 num = 2;
3445 off = 0;
3446 } else
3447 {
3448 string_vector tokens;
3449 split_string(param, tokens, _t(" n"), _t("n"));
3450
3451 tstring s_num;
3452 tstring s_off;
3453
3454 tstring s_int;
3455 for(string_vector::iterator tok = tokens.begin(); tok != tokens.end(); tok++)
3456 {
3457 if((*tok) == _t("n"))
3458 {
3459 s_num = s_int;
3460 s_int.clear();
3461 } else
3462 {
3463 s_int += (*tok);
3464 }
3465 }
3466 s_off = s_int;
3467
3468 num = t_atoi(s_num.c_str());
3469 off = t_atoi(s_off.c_str());
3470 }
3471 }
3472
calc_document_size(litehtml::size & sz,int x,int y)3473 void litehtml::html_tag::calc_document_size( litehtml::size& sz, int x /*= 0*/, int y /*= 0*/ )
3474 {
3475 if(is_visible() && m_el_position != element_position_fixed)
3476 {
3477 element::calc_document_size(sz, x, y);
3478
3479 if(m_overflow == overflow_visible)
3480 {
3481 for(auto& el : m_children)
3482 {
3483 el->calc_document_size(sz, x + m_pos.x, y + m_pos.y);
3484 }
3485 }
3486
3487 // root element (<html>) must to cover entire window
3488 if(!have_parent())
3489 {
3490 position client_pos;
3491 get_document()->container()->get_client_rect(client_pos);
3492 m_pos.height = std::max(sz.height, client_pos.height) - content_margins_top() - content_margins_bottom();
3493 m_pos.width = std::max(sz.width, client_pos.width) - content_margins_left() - content_margins_right();
3494 }
3495 }
3496 }
3497
3498
get_redraw_box(litehtml::position & pos,int x,int y)3499 void litehtml::html_tag::get_redraw_box(litehtml::position& pos, int x /*= 0*/, int y /*= 0*/)
3500 {
3501 if(is_visible())
3502 {
3503 element::get_redraw_box(pos, x, y);
3504
3505 if(m_overflow == overflow_visible)
3506 {
3507 for(auto& el : m_children)
3508 {
3509 if(el->get_element_position() != element_position_fixed)
3510 {
3511 el->get_redraw_box(pos, x + m_pos.x, y + m_pos.y);
3512 }
3513 }
3514 }
3515 }
3516 }
3517
find_adjacent_sibling(const element::ptr & el,const css_selector & selector,bool apply_pseudo,bool * is_pseudo)3518 litehtml::element::ptr litehtml::html_tag::find_adjacent_sibling( const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/ )
3519 {
3520 element::ptr ret;
3521 for(auto& e : m_children)
3522 {
3523 if(e->get_display() != display_inline_text)
3524 {
3525 if(e == el)
3526 {
3527 if(ret)
3528 {
3529 int res = ret->select(selector, apply_pseudo);
3530 if(res != select_no_match)
3531 {
3532 if(is_pseudo)
3533 {
3534 if(res & select_match_pseudo_class)
3535 {
3536 *is_pseudo = true;
3537 } else
3538 {
3539 *is_pseudo = false;
3540 }
3541 }
3542 return ret;
3543 }
3544 }
3545 return 0;
3546 } else
3547 {
3548 ret = e;
3549 }
3550 }
3551 }
3552 return 0;
3553 }
3554
find_sibling(const element::ptr & el,const css_selector & selector,bool apply_pseudo,bool * is_pseudo)3555 litehtml::element::ptr litehtml::html_tag::find_sibling(const element::ptr& el, const css_selector& selector, bool apply_pseudo /*= true*/, bool* is_pseudo /*= 0*/)
3556 {
3557 element::ptr ret = 0;
3558 for(auto& e : m_children)
3559 {
3560 if(e->get_display() != display_inline_text)
3561 {
3562 if(e == el)
3563 {
3564 return ret;
3565 } else if(!ret)
3566 {
3567 int res = e->select(selector, apply_pseudo);
3568 if(res != select_no_match)
3569 {
3570 if(is_pseudo)
3571 {
3572 if(res & select_match_pseudo_class)
3573 {
3574 *is_pseudo = true;
3575 } else
3576 {
3577 *is_pseudo = false;
3578 }
3579 }
3580 ret = e;
3581 }
3582 }
3583 }
3584 }
3585 return 0;
3586 }
3587
is_only_child(const element::ptr & el,bool of_type) const3588 bool litehtml::html_tag::is_only_child(const element::ptr& el, bool of_type) const
3589 {
3590 int child_count = 0;
3591 for(const auto& child : m_children)
3592 {
3593 if(child->get_display() != display_inline_text)
3594 {
3595 if( !of_type || (of_type && !t_strcmp(el->get_tagName(), child->get_tagName())) )
3596 {
3597 child_count++;
3598 }
3599 if(child_count > 1) break;
3600 }
3601 }
3602 if(child_count > 1)
3603 {
3604 return false;
3605 }
3606 return true;
3607 }
3608
update_floats(int dy,const element::ptr & parent)3609 void litehtml::html_tag::update_floats(int dy, const element::ptr &parent)
3610 {
3611 if(is_floats_holder())
3612 {
3613 bool reset_cache = false;
3614 for(floated_box::vector::reverse_iterator fb = m_floats_left.rbegin(); fb != m_floats_left.rend(); fb++)
3615 {
3616 if(fb->el->is_ancestor(parent))
3617 {
3618 reset_cache = true;
3619 fb->pos.y += dy;
3620 }
3621 }
3622 if(reset_cache)
3623 {
3624 m_cahe_line_left.invalidate();
3625 }
3626 reset_cache = false;
3627 for(floated_box::vector::reverse_iterator fb = m_floats_right.rbegin(); fb != m_floats_right.rend(); fb++)
3628 {
3629 if(fb->el->is_ancestor(parent))
3630 {
3631 reset_cache = true;
3632 fb->pos.y += dy;
3633 }
3634 }
3635 if(reset_cache)
3636 {
3637 m_cahe_line_right.invalidate();
3638 }
3639 } else
3640 {
3641 element::ptr el_parent = this->parent();
3642 if (el_parent)
3643 {
3644 el_parent->update_floats(dy, parent);
3645 }
3646 }
3647 }
3648
remove_before_after()3649 void litehtml::html_tag::remove_before_after()
3650 {
3651 if(!m_children.empty())
3652 {
3653 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3654 {
3655 m_children.erase(m_children.begin());
3656 }
3657 }
3658 if(!m_children.empty())
3659 {
3660 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3661 {
3662 m_children.erase(m_children.end() - 1);
3663 }
3664 }
3665 }
3666
get_element_before()3667 litehtml::element::ptr litehtml::html_tag::get_element_before()
3668 {
3669 if(!m_children.empty())
3670 {
3671 if( !t_strcmp(m_children.front()->get_tagName(), _t("::before")) )
3672 {
3673 return m_children.front();
3674 }
3675 }
3676 element::ptr el = std::make_shared<el_before>(get_document());
3677 el->parent(shared_from_this());
3678 m_children.insert(m_children.begin(), el);
3679 return el;
3680 }
3681
get_element_after()3682 litehtml::element::ptr litehtml::html_tag::get_element_after()
3683 {
3684 if(!m_children.empty())
3685 {
3686 if( !t_strcmp(m_children.back()->get_tagName(), _t("::after")) )
3687 {
3688 return m_children.back();
3689 }
3690 }
3691 element::ptr el = std::make_shared<el_after>(get_document());
3692 appendChild(el);
3693 return el;
3694 }
3695
add_style(const litehtml::style & st)3696 void litehtml::html_tag::add_style( const litehtml::style& st )
3697 {
3698 m_style.combine(st);
3699 }
3700
have_inline_child() const3701 bool litehtml::html_tag::have_inline_child() const
3702 {
3703 if(!m_children.empty())
3704 {
3705 for(const auto& el : m_children)
3706 {
3707 if(!el->is_white_space())
3708 {
3709 return true;
3710 }
3711 }
3712 }
3713 return false;
3714 }
3715
refresh_styles()3716 void litehtml::html_tag::refresh_styles()
3717 {
3718 remove_before_after();
3719
3720 for (auto& el : m_children)
3721 {
3722 if(el->get_display() != display_inline_text)
3723 {
3724 el->refresh_styles();
3725 }
3726 }
3727
3728 m_style.clear();
3729
3730 for (auto& usel : m_used_styles)
3731 {
3732 usel->m_used = false;
3733
3734 if(usel->m_selector->is_media_valid())
3735 {
3736 int apply = select(*usel->m_selector, false);
3737
3738 if(apply != select_no_match)
3739 {
3740 if(apply & select_match_pseudo_class)
3741 {
3742 if(select(*usel->m_selector, true))
3743 {
3744 if(apply & select_match_with_after)
3745 {
3746 element::ptr el = get_element_after();
3747 if(el)
3748 {
3749 el->add_style(*usel->m_selector->m_style);
3750 }
3751 } else if(apply & select_match_with_before)
3752 {
3753 element::ptr el = get_element_before();
3754 if(el)
3755 {
3756 el->add_style(*usel->m_selector->m_style);
3757 }
3758 }
3759 else
3760 {
3761 add_style(*usel->m_selector->m_style);
3762 usel->m_used = true;
3763 }
3764 }
3765 } else if(apply & select_match_with_after)
3766 {
3767 element::ptr el = get_element_after();
3768 if(el)
3769 {
3770 el->add_style(*usel->m_selector->m_style);
3771 }
3772 } else if(apply & select_match_with_before)
3773 {
3774 element::ptr el = get_element_before();
3775 if(el)
3776 {
3777 el->add_style(*usel->m_selector->m_style);
3778 }
3779 } else
3780 {
3781 add_style(*usel->m_selector->m_style);
3782 usel->m_used = true;
3783 }
3784 }
3785 }
3786 }
3787 }
3788
get_child_by_point(int x,int y,int client_x,int client_y,draw_flag flag,int zindex)3789 litehtml::element::ptr litehtml::html_tag::get_child_by_point(int x, int y, int client_x, int client_y, draw_flag flag, int zindex)
3790 {
3791 element::ptr ret = 0;
3792
3793 if(m_overflow > overflow_visible)
3794 {
3795 if(!m_pos.is_point_inside(x, y))
3796 {
3797 return ret;
3798 }
3799 }
3800
3801 position pos = m_pos;
3802 pos.x = x - pos.x;
3803 pos.y = y - pos.y;
3804
3805 for(elements_vector::reverse_iterator i = m_children.rbegin(); i != m_children.rend() && !ret; i++)
3806 {
3807 element::ptr el = (*i);
3808
3809 if(el->is_visible() && el->get_display() != display_inline_text)
3810 {
3811 switch(flag)
3812 {
3813 case draw_positioned:
3814 if(el->is_positioned() && el->get_zindex() == zindex)
3815 {
3816 if(el->get_element_position() == element_position_fixed)
3817 {
3818 ret = el->get_element_by_point(client_x, client_y, client_x, client_y);
3819 if(!ret && (*i)->is_point_inside(client_x, client_y))
3820 {
3821 ret = (*i);
3822 }
3823 } else
3824 {
3825 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3826 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3827 {
3828 ret = (*i);
3829 }
3830 }
3831 el = 0;
3832 }
3833 break;
3834 case draw_block:
3835 if(!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3836 {
3837 if(el->is_point_inside(pos.x, pos.y))
3838 {
3839 ret = el;
3840 }
3841 }
3842 break;
3843 case draw_floats:
3844 if(el->get_float() != float_none && !el->is_positioned())
3845 {
3846 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3847
3848 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3849 {
3850 ret = (*i);
3851 }
3852 el = 0;
3853 }
3854 break;
3855 case draw_inlines:
3856 if(el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
3857 {
3858 if(el->get_display() == display_inline_block)
3859 {
3860 ret = el->get_element_by_point(pos.x, pos.y, client_x, client_y);
3861 el = 0;
3862 }
3863 if(!ret && (*i)->is_point_inside(pos.x, pos.y))
3864 {
3865 ret = (*i);
3866 }
3867 }
3868 break;
3869 default:
3870 break;
3871 }
3872
3873 if(el && !el->is_positioned())
3874 {
3875 if(flag == draw_positioned)
3876 {
3877 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3878 if(child)
3879 {
3880 ret = child;
3881 }
3882 } else
3883 {
3884 if( el->get_float() == float_none &&
3885 el->get_display() != display_inline_block)
3886 {
3887 element::ptr child = el->get_child_by_point(pos.x, pos.y, client_x, client_y, flag, zindex);
3888 if(child)
3889 {
3890 ret = child;
3891 }
3892 }
3893 }
3894 }
3895 }
3896 }
3897
3898 return ret;
3899 }
3900
get_element_by_point(int x,int y,int client_x,int client_y)3901 litehtml::element::ptr litehtml::html_tag::get_element_by_point(int x, int y, int client_x, int client_y)
3902 {
3903 if(!is_visible()) return 0;
3904
3905 element::ptr ret;
3906
3907 std::map<int, bool> zindexes;
3908
3909 for(elements_vector::iterator i = m_positioned.begin(); i != m_positioned.end(); i++)
3910 {
3911 zindexes[(*i)->get_zindex()];
3912 }
3913
3914 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3915 {
3916 if(idx->first > 0)
3917 {
3918 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3919 }
3920 }
3921 if(ret) return ret;
3922
3923 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3924 {
3925 if(idx->first == 0)
3926 {
3927 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3928 }
3929 }
3930 if(ret) return ret;
3931
3932 ret = get_child_by_point(x, y, client_x, client_y, draw_inlines, 0);
3933 if(ret) return ret;
3934
3935 ret = get_child_by_point(x, y, client_x, client_y, draw_floats, 0);
3936 if(ret) return ret;
3937
3938 ret = get_child_by_point(x, y, client_x, client_y, draw_block, 0);
3939 if(ret) return ret;
3940
3941
3942 for(std::map<int, bool>::iterator idx = zindexes.begin(); idx != zindexes.end() && !ret; idx++)
3943 {
3944 if(idx->first < 0)
3945 {
3946 ret = get_child_by_point(x, y, client_x, client_y, draw_positioned, idx->first);
3947 }
3948 }
3949 if(ret) return ret;
3950
3951 if(m_el_position == element_position_fixed)
3952 {
3953 if(is_point_inside(client_x, client_y))
3954 {
3955 ret = shared_from_this();
3956 }
3957 } else
3958 {
3959 if(is_point_inside(x, y))
3960 {
3961 ret = shared_from_this();
3962 }
3963 }
3964
3965 return ret;
3966 }
3967
get_background(bool own_only)3968 const litehtml::background* litehtml::html_tag::get_background(bool own_only)
3969 {
3970 if(own_only)
3971 {
3972 // return own background with check for empty one
3973 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3974 {
3975 return 0;
3976 }
3977 return &m_bg;
3978 }
3979
3980 if(m_bg.m_image.empty() && !m_bg.m_color.alpha)
3981 {
3982 // if this is root element (<html>) try to get background from body
3983 if (!have_parent())
3984 {
3985 for (const auto& el : m_children)
3986 {
3987 if( el->is_body() )
3988 {
3989 // return own body background
3990 return el->get_background(true);
3991 }
3992 }
3993 }
3994 return 0;
3995 }
3996
3997 if(is_body())
3998 {
3999 element::ptr el_parent = parent();
4000 if (el_parent)
4001 {
4002 if (!el_parent->get_background(true))
4003 {
4004 // parent of body will draw background for body
4005 return 0;
4006 }
4007 }
4008 }
4009
4010 return &m_bg;
4011 }
4012
render_box(int x,int y,int max_width,bool second_pass)4013 int litehtml::html_tag::render_box(int x, int y, int max_width, bool second_pass /*= false*/)
4014 {
4015 int parent_width = max_width;
4016
4017 calc_outlines(parent_width);
4018
4019 m_pos.clear();
4020 m_pos.move_to(x, y);
4021
4022 m_pos.x += content_margins_left();
4023 m_pos.y += content_margins_top();
4024
4025 int ret_width = 0;
4026
4027 def_value<int> block_width(0);
4028
4029 if (m_display != display_table_cell && !m_css_width.is_predefined())
4030 {
4031 int w = calc_width(parent_width);
4032
4033 if (m_box_sizing == box_sizing_border_box)
4034 {
4035 w -= m_padding.width() + m_borders.width();
4036 }
4037 ret_width = max_width = block_width = w;
4038 }
4039 else
4040 {
4041 if (max_width)
4042 {
4043 max_width -= content_margins_left() + content_margins_right();
4044 }
4045 }
4046
4047 // check for max-width (on the first pass only)
4048 if (!m_css_max_width.is_predefined() && !second_pass)
4049 {
4050 int mw = get_document()->cvt_units(m_css_max_width, m_font_size, parent_width);
4051 if (m_box_sizing == box_sizing_border_box)
4052 {
4053 mw -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
4054 }
4055 if (max_width > mw)
4056 {
4057 max_width = mw;
4058 }
4059 }
4060
4061 m_floats_left.clear();
4062 m_floats_right.clear();
4063 m_boxes.clear();
4064 m_cahe_line_left.invalidate();
4065 m_cahe_line_right.invalidate();
4066
4067 element_position el_position;
4068
4069 int block_height = 0;
4070
4071 m_pos.height = 0;
4072
4073 if (get_predefined_height(block_height))
4074 {
4075 m_pos.height = block_height;
4076 }
4077
4078 white_space ws = get_white_space();
4079 bool skip_spaces = false;
4080 if (ws == white_space_normal ||
4081 ws == white_space_nowrap ||
4082 ws == white_space_pre_line)
4083 {
4084 skip_spaces = true;
4085 }
4086
4087 bool was_space = false;
4088
4089 for (auto el : m_children)
4090 {
4091 // we don't need process absolute and fixed positioned element on the second pass
4092 if (second_pass)
4093 {
4094 el_position = el->get_element_position();
4095 if ((el_position == element_position_absolute || el_position == element_position_fixed)) continue;
4096 }
4097
4098 // skip spaces to make rendering a bit faster
4099 if (skip_spaces)
4100 {
4101 if (el->is_white_space())
4102 {
4103 if (was_space)
4104 {
4105 el->skip(true);
4106 continue;
4107 }
4108 else
4109 {
4110 was_space = true;
4111 }
4112 }
4113 else
4114 {
4115 was_space = false;
4116 }
4117 }
4118
4119 // place element into rendering flow
4120 int rw = place_element(el, max_width);
4121 if (rw > ret_width)
4122 {
4123 ret_width = rw;
4124 }
4125 }
4126
4127 finish_last_box(true);
4128
4129 if (block_width.is_default() && is_inline_box())
4130 {
4131 m_pos.width = ret_width;
4132 }
4133 else
4134 {
4135 m_pos.width = max_width;
4136 }
4137 calc_auto_margins(parent_width);
4138
4139 if (!m_boxes.empty())
4140 {
4141 if (collapse_top_margin())
4142 {
4143 int old_top = m_margins.top;
4144 m_margins.top = std::max(m_boxes.front()->top_margin(), m_margins.top);
4145 if (m_margins.top != old_top)
4146 {
4147 update_floats(m_margins.top - old_top, shared_from_this());
4148 }
4149 }
4150 if (collapse_bottom_margin())
4151 {
4152 m_margins.bottom = std::max(m_boxes.back()->bottom_margin(), m_margins.bottom);
4153 m_pos.height = m_boxes.back()->bottom() - m_boxes.back()->bottom_margin();
4154 }
4155 else
4156 {
4157 m_pos.height = m_boxes.back()->bottom();
4158 }
4159 }
4160
4161 // add the floats height to the block height
4162 if (is_floats_holder())
4163 {
4164 int floats_height = get_floats_height();
4165 if (floats_height > m_pos.height)
4166 {
4167 m_pos.height = floats_height;
4168 }
4169 }
4170
4171 // calculate the final position
4172
4173 m_pos.move_to(x, y);
4174 m_pos.x += content_margins_left();
4175 m_pos.y += content_margins_top();
4176
4177 if (get_predefined_height(block_height))
4178 {
4179 m_pos.height = block_height;
4180 }
4181
4182 int min_height = 0;
4183 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4184 {
4185 element::ptr el_parent = parent();
4186 if (el_parent)
4187 {
4188 if (el_parent->get_predefined_height(block_height))
4189 {
4190 min_height = m_css_min_height.calc_percent(block_height);
4191 }
4192 }
4193 }
4194 else
4195 {
4196 min_height = (int)m_css_min_height.val();
4197 }
4198 if (min_height != 0 && m_box_sizing == box_sizing_border_box)
4199 {
4200 min_height -= m_padding.top + m_borders.top + m_padding.bottom + m_borders.bottom;
4201 if (min_height < 0) min_height = 0;
4202 }
4203
4204 if (m_display == display_list_item)
4205 {
4206 const tchar_t* list_image = get_style_property(_t("list-style-image"), true, 0);
4207 if (list_image)
4208 {
4209 tstring url;
4210 css::parse_css_url(list_image, url);
4211
4212 size sz;
4213 const tchar_t* list_image_baseurl = get_style_property(_t("list-style-image-baseurl"), true, 0);
4214 get_document()->container()->get_image_size(url.c_str(), list_image_baseurl, sz);
4215 if (min_height < sz.height)
4216 {
4217 min_height = sz.height;
4218 }
4219 }
4220
4221 }
4222
4223 if (min_height > m_pos.height)
4224 {
4225 m_pos.height = min_height;
4226 }
4227
4228 int min_width = m_css_min_width.calc_percent(parent_width);
4229
4230 if (min_width != 0 && m_box_sizing == box_sizing_border_box)
4231 {
4232 min_width -= m_padding.left + m_borders.left + m_padding.right + m_borders.right;
4233 if (min_width < 0) min_width = 0;
4234 }
4235
4236 if (min_width != 0)
4237 {
4238 if (min_width > m_pos.width)
4239 {
4240 m_pos.width = min_width;
4241 }
4242 if (min_width > ret_width)
4243 {
4244 ret_width = min_width;
4245 }
4246 }
4247
4248 ret_width += content_margins_left() + content_margins_right();
4249
4250 // re-render with new width
4251 if (ret_width < max_width && !second_pass && have_parent())
4252 {
4253 if (m_display == display_inline_block ||
4254 (m_css_width.is_predefined() &&
4255 (m_float != float_none ||
4256 m_display == display_table ||
4257 m_el_position == element_position_absolute ||
4258 m_el_position == element_position_fixed
4259 ))
4260 )
4261 {
4262 render(x, y, ret_width, true);
4263 m_pos.width = ret_width - (content_margins_left() + content_margins_right());
4264 }
4265 }
4266
4267 if (is_floats_holder() && !second_pass)
4268 {
4269 for (const auto& fb : m_floats_left)
4270 {
4271 fb.el->apply_relative_shift(fb.el->parent()->calc_width(m_pos.width));
4272 }
4273 }
4274
4275
4276 return ret_width;
4277 }
4278
render_table(int x,int y,int max_width,bool second_pass)4279 int litehtml::html_tag::render_table(int x, int y, int max_width, bool second_pass /*= false*/)
4280 {
4281 if (!m_grid) return 0;
4282
4283 int parent_width = max_width;
4284
4285 calc_outlines(parent_width);
4286
4287 m_pos.clear();
4288 m_pos.move_to(x, y);
4289
4290 m_pos.x += content_margins_left();
4291 m_pos.y += content_margins_top();
4292
4293 def_value<int> block_width(0);
4294
4295 if (!m_css_width.is_predefined())
4296 {
4297 max_width = block_width = calc_width(parent_width) - m_padding.width() - m_borders.width();
4298 }
4299 else
4300 {
4301 if (max_width)
4302 {
4303 max_width -= content_margins_left() + content_margins_right();
4304 }
4305 }
4306
4307 // Calculate table spacing
4308 int table_width_spacing = 0;
4309 if (m_border_collapse == border_collapse_separate)
4310 {
4311 table_width_spacing = m_border_spacing_x * (m_grid->cols_count() + 1);
4312 }
4313 else
4314 {
4315 table_width_spacing = 0;
4316
4317 if (m_grid->cols_count())
4318 {
4319 table_width_spacing -= std::min(border_left(), m_grid->column(0).border_left);
4320 table_width_spacing -= std::min(border_right(), m_grid->column(m_grid->cols_count() - 1).border_right);
4321 }
4322
4323 for (int col = 1; col < m_grid->cols_count(); col++)
4324 {
4325 table_width_spacing -= std::min(m_grid->column(col).border_left, m_grid->column(col - 1).border_right);
4326 }
4327 }
4328
4329
4330 // Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
4331 // If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum
4332 // cell width.
4333 //
4334 // Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
4335
4336 if (m_grid->cols_count() == 1 && !block_width.is_default())
4337 {
4338 for (int row = 0; row < m_grid->rows_count(); row++)
4339 {
4340 table_cell* cell = m_grid->cell(0, row);
4341 if (cell && cell->el)
4342 {
4343 cell->min_width = cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4344 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4345 }
4346 }
4347 }
4348 else
4349 {
4350 for (int row = 0; row < m_grid->rows_count(); row++)
4351 {
4352 for (int col = 0; col < m_grid->cols_count(); col++)
4353 {
4354 table_cell* cell = m_grid->cell(col, row);
4355 if (cell && cell->el)
4356 {
4357 if (!m_grid->column(col).css_width.is_predefined() && m_grid->column(col).css_width.units() != css_units_percentage)
4358 {
4359 int css_w = m_grid->column(col).css_width.calc_percent(block_width);
4360 int el_w = cell->el->render(0, 0, css_w);
4361 cell->min_width = cell->max_width = std::max(css_w, el_w);
4362 cell->el->m_pos.width = cell->min_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4363 }
4364 else
4365 {
4366 // calculate minimum content width
4367 cell->min_width = cell->el->render(0, 0, 1);
4368 // calculate maximum content width
4369 cell->max_width = cell->el->render(0, 0, max_width - table_width_spacing);
4370 }
4371 }
4372 }
4373 }
4374 }
4375
4376 // For each column, determine a maximum and minimum column width from the cells that span only that column.
4377 // The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger).
4378 // The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).
4379
4380 for (int col = 0; col < m_grid->cols_count(); col++)
4381 {
4382 m_grid->column(col).max_width = 0;
4383 m_grid->column(col).min_width = 0;
4384 for (int row = 0; row < m_grid->rows_count(); row++)
4385 {
4386 if (m_grid->cell(col, row)->colspan <= 1)
4387 {
4388 m_grid->column(col).max_width = std::max(m_grid->column(col).max_width, m_grid->cell(col, row)->max_width);
4389 m_grid->column(col).min_width = std::max(m_grid->column(col).min_width, m_grid->cell(col, row)->min_width);
4390 }
4391 }
4392 }
4393
4394 // For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together,
4395 // they are at least as wide as the cell. Do the same for the maximum widths.
4396 // If possible, widen all spanned columns by approximately the same amount.
4397
4398 for (int col = 0; col < m_grid->cols_count(); col++)
4399 {
4400 for (int row = 0; row < m_grid->rows_count(); row++)
4401 {
4402 if (m_grid->cell(col, row)->colspan > 1)
4403 {
4404 int max_total_width = m_grid->column(col).max_width;
4405 int min_total_width = m_grid->column(col).min_width;
4406 for (int col2 = col + 1; col2 < col + m_grid->cell(col, row)->colspan; col2++)
4407 {
4408 max_total_width += m_grid->column(col2).max_width;
4409 min_total_width += m_grid->column(col2).min_width;
4410 }
4411 if (min_total_width < m_grid->cell(col, row)->min_width)
4412 {
4413 m_grid->distribute_min_width(m_grid->cell(col, row)->min_width - min_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4414 }
4415 if (max_total_width < m_grid->cell(col, row)->max_width)
4416 {
4417 m_grid->distribute_max_width(m_grid->cell(col, row)->max_width - max_total_width, col, col + m_grid->cell(col, row)->colspan - 1);
4418 }
4419 }
4420 }
4421 }
4422
4423 // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the
4424 // greater of W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
4425 // If the used width is greater than MIN, the extra width should be distributed over the columns.
4426 //
4427 // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
4428 // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
4429 // less than that of the containing block, use max(MAX, CAPMIN).
4430
4431
4432 int table_width = 0;
4433 int min_table_width = 0;
4434 int max_table_width = 0;
4435
4436 if (!block_width.is_default())
4437 {
4438 table_width = m_grid->calc_table_width(block_width - table_width_spacing, false, min_table_width, max_table_width);
4439 }
4440 else
4441 {
4442 table_width = m_grid->calc_table_width(max_width - table_width_spacing, true, min_table_width, max_table_width);
4443 }
4444
4445 min_table_width += table_width_spacing;
4446 max_table_width += table_width_spacing;
4447 table_width += table_width_spacing;
4448 m_grid->calc_horizontal_positions(m_borders, m_border_collapse, m_border_spacing_x);
4449
4450 bool row_span_found = false;
4451
4452 // render cells with computed width
4453 for (int row = 0; row < m_grid->rows_count(); row++)
4454 {
4455 m_grid->row(row).height = 0;
4456 for (int col = 0; col < m_grid->cols_count(); col++)
4457 {
4458 table_cell* cell = m_grid->cell(col, row);
4459 if (cell->el)
4460 {
4461 int span_col = col + cell->colspan - 1;
4462 if (span_col >= m_grid->cols_count())
4463 {
4464 span_col = m_grid->cols_count() - 1;
4465 }
4466 int cell_width = m_grid->column(span_col).right - m_grid->column(col).left;
4467
4468 if (cell->el->m_pos.width != cell_width - cell->el->content_margins_left() - cell->el->content_margins_right())
4469 {
4470 cell->el->render(m_grid->column(col).left, 0, cell_width);
4471 cell->el->m_pos.width = cell_width - cell->el->content_margins_left() - cell->el->content_margins_right();
4472 }
4473 else
4474 {
4475 cell->el->m_pos.x = m_grid->column(col).left + cell->el->content_margins_left();
4476 }
4477
4478 if (cell->rowspan <= 1)
4479 {
4480 m_grid->row(row).height = std::max(m_grid->row(row).height, cell->el->height());
4481 }
4482 else
4483 {
4484 row_span_found = true;
4485 }
4486
4487 }
4488 }
4489 }
4490
4491 if (row_span_found)
4492 {
4493 for (int col = 0; col < m_grid->cols_count(); col++)
4494 {
4495 for (int row = 0; row < m_grid->rows_count(); row++)
4496 {
4497 table_cell* cell = m_grid->cell(col, row);
4498 if (cell->el)
4499 {
4500 int span_row = row + cell->rowspan - 1;
4501 if (span_row >= m_grid->rows_count())
4502 {
4503 span_row = m_grid->rows_count() - 1;
4504 }
4505 if (span_row != row)
4506 {
4507 int h = 0;
4508 for (int i = row; i <= span_row; i++)
4509 {
4510 h += m_grid->row(i).height;
4511 }
4512 if (h < cell->el->height())
4513 {
4514 m_grid->row(span_row).height += cell->el->height() - h;
4515 }
4516 }
4517 }
4518 }
4519 }
4520 }
4521
4522 // Calculate vertical table spacing
4523 int table_height_spacing = 0;
4524 if (m_border_collapse == border_collapse_separate)
4525 {
4526 table_height_spacing = m_border_spacing_y * (m_grid->rows_count() + 1);
4527 }
4528 else
4529 {
4530 table_height_spacing = 0;
4531
4532 if (m_grid->rows_count())
4533 {
4534 table_height_spacing -= std::min(border_top(), m_grid->row(0).border_top);
4535 table_height_spacing -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4536 }
4537
4538 for (int row = 1; row < m_grid->rows_count(); row++)
4539 {
4540 table_height_spacing -= std::min(m_grid->row(row).border_top, m_grid->row(row - 1).border_bottom);
4541 }
4542 }
4543
4544
4545 // calculate block height
4546 int block_height = 0;
4547 if (get_predefined_height(block_height))
4548 {
4549 block_height -= m_padding.height() + m_borders.height();
4550 }
4551
4552 // calculate minimum height from m_css_min_height
4553 int min_height = 0;
4554 if (!m_css_min_height.is_predefined() && m_css_min_height.units() == css_units_percentage)
4555 {
4556 element::ptr el_parent = parent();
4557 if (el_parent)
4558 {
4559 int parent_height = 0;
4560 if (el_parent->get_predefined_height(parent_height))
4561 {
4562 min_height = m_css_min_height.calc_percent(parent_height);
4563 }
4564 }
4565 }
4566 else
4567 {
4568 min_height = (int)m_css_min_height.val();
4569 }
4570
4571 int extra_row_height = 0;
4572 int minimum_table_height = std::max(block_height, min_height);
4573
4574 m_grid->calc_rows_height(minimum_table_height - table_height_spacing, m_border_spacing_y);
4575 m_grid->calc_vertical_positions(m_borders, m_border_collapse, m_border_spacing_y);
4576
4577 int table_height = 0;
4578
4579 // place cells vertically
4580 for (int col = 0; col < m_grid->cols_count(); col++)
4581 {
4582 for (int row = 0; row < m_grid->rows_count(); row++)
4583 {
4584 table_cell* cell = m_grid->cell(col, row);
4585 if (cell->el)
4586 {
4587 int span_row = row + cell->rowspan - 1;
4588 if (span_row >= m_grid->rows_count())
4589 {
4590 span_row = m_grid->rows_count() - 1;
4591 }
4592 cell->el->m_pos.y = m_grid->row(row).top + cell->el->content_margins_top();
4593 cell->el->m_pos.height = m_grid->row(span_row).bottom - m_grid->row(row).top - cell->el->content_margins_top() - cell->el->content_margins_bottom();
4594 table_height = std::max(table_height, m_grid->row(span_row).bottom);
4595 cell->el->apply_vertical_align();
4596 }
4597 }
4598 }
4599
4600 if (m_border_collapse == border_collapse_collapse)
4601 {
4602 if (m_grid->rows_count())
4603 {
4604 table_height -= std::min(border_bottom(), m_grid->row(m_grid->rows_count() - 1).border_bottom);
4605 }
4606 }
4607 else
4608 {
4609 table_height += m_border_spacing_y;
4610 }
4611
4612 m_pos.width = table_width;
4613
4614 calc_auto_margins(parent_width);
4615
4616 m_pos.move_to(x, y);
4617 m_pos.x += content_margins_left();
4618 m_pos.y += content_margins_top();
4619 m_pos.width = table_width;
4620 m_pos.height = table_height;
4621
4622 return max_table_width;
4623 }
4624
draw_children_box(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)4625 void litehtml::html_tag::draw_children_box(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4626 {
4627 position pos = m_pos;
4628 pos.x += x;
4629 pos.y += y;
4630
4631 document::ptr doc = get_document();
4632
4633 if (m_overflow > overflow_visible)
4634 {
4635 position border_box = pos;
4636 border_box += m_padding;
4637 border_box += m_borders;
4638
4639 border_radiuses bdr_radius = m_css_borders.radius.calc_percents(border_box.width, border_box.height);
4640
4641 bdr_radius -= m_borders;
4642 bdr_radius -= m_padding;
4643
4644 doc->container()->set_clip(pos, bdr_radius, true, true);
4645 }
4646
4647 position browser_wnd;
4648 doc->container()->get_client_rect(browser_wnd);
4649
4650 element::ptr el;
4651 for (auto& item : m_children)
4652 {
4653 el = item;
4654 if (el->is_visible())
4655 {
4656 switch (flag)
4657 {
4658 case draw_positioned:
4659 if (el->is_positioned() && el->get_zindex() == zindex)
4660 {
4661 if (el->get_element_position() == element_position_fixed)
4662 {
4663 el->draw(hdc, browser_wnd.x, browser_wnd.y, clip);
4664 el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true);
4665 }
4666 else
4667 {
4668 el->draw(hdc, pos.x, pos.y, clip);
4669 el->draw_stacking_context(hdc, pos.x, pos.y, clip, true);
4670 }
4671 el = 0;
4672 }
4673 break;
4674 case draw_block:
4675 if (!el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4676 {
4677 el->draw(hdc, pos.x, pos.y, clip);
4678 }
4679 break;
4680 case draw_floats:
4681 if (el->get_float() != float_none && !el->is_positioned())
4682 {
4683 el->draw(hdc, pos.x, pos.y, clip);
4684 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4685 el = 0;
4686 }
4687 break;
4688 case draw_inlines:
4689 if (el->is_inline_box() && el->get_float() == float_none && !el->is_positioned())
4690 {
4691 el->draw(hdc, pos.x, pos.y, clip);
4692 if (el->get_display() == display_inline_block)
4693 {
4694 el->draw_stacking_context(hdc, pos.x, pos.y, clip, false);
4695 el = 0;
4696 }
4697 }
4698 break;
4699 default:
4700 break;
4701 }
4702
4703 if (el)
4704 {
4705 if (flag == draw_positioned)
4706 {
4707 if (!el->is_positioned())
4708 {
4709 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4710 }
4711 }
4712 else
4713 {
4714 if (el->get_float() == float_none &&
4715 el->get_display() != display_inline_block &&
4716 !el->is_positioned())
4717 {
4718 el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4719 }
4720 }
4721 }
4722 }
4723 }
4724
4725 if (m_overflow > overflow_visible)
4726 {
4727 doc->container()->del_clip();
4728 }
4729 }
4730
draw_children_table(uint_ptr hdc,int x,int y,const position * clip,draw_flag flag,int zindex)4731 void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const position* clip, draw_flag flag, int zindex)
4732 {
4733 if (!m_grid) return;
4734
4735 position pos = m_pos;
4736 pos.x += x;
4737 pos.y += y;
4738 for (int row = 0; row < m_grid->rows_count(); row++)
4739 {
4740 if (flag == draw_block)
4741 {
4742 m_grid->row(row).el_row->draw_background(hdc, pos.x, pos.y, clip);
4743 }
4744 for (int col = 0; col < m_grid->cols_count(); col++)
4745 {
4746 table_cell* cell = m_grid->cell(col, row);
4747 if (cell->el)
4748 {
4749 if (flag == draw_block)
4750 {
4751 cell->el->draw(hdc, pos.x, pos.y, clip);
4752 }
4753 cell->el->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
4754 }
4755 }
4756 }
4757 }
4758