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